对相同 Class 类型的 C++ 引用公开私有成员

C++ Reference to Same Class Type Exposes Private Members

此处对此进行更长时间的讨论:Why do objects of the same class have access to each other's private data?

使用具有整数长度的 Line 对象的简单示例。 operator+ 重载函数可以访问另一行的私有长度(参数 const Line &line,也就是添加到 this 行的行)。对于非运算符重载函数 (printOtherLine) 和友元函数 (printFriendLine) 也是如此。再次作为参数传递的 Line 不是 this 对象。

为什么会这样?

#include <iostream>

class Line
{
   public:
      Line()
      {
         length = 0;
      }
      Line(int length)
      {
         this->length = length;
      }
      Line operator+(const Line &line)
      {
         Line newLine(this->length + line.length); // I would have thought 
                                                  // this would be line.getLength()
                                                  // instead of line.length
         return newLine;
      }
      int getLength()
      {
         return length;
      }
      void printOtherLine(const Line &otherLine){
         std::cout << "Other Line: " << otherLine.length << std::endl;
      }
      void printLine(int lineNumber){
         std::cout << "Line " << lineNumber << ": " << this->length << std::endl;
      }
      friend void printFriendLine(const Line &friendlyLine);
   private:
      int length;
};

void printFriendLine(const Line &friendlyLine){
   std::cout << "Friendly Line: " << friendlyLine.length << std::endl;
}

// This function will not compile
// void printUnassociatedLine(const Line &line){
//    std::cout << "Unassociated Line: " << line.length << std::endl;
// }

int main()
{
   Line l1(10);
   l1.printLine(1);
   Line l2(15);
   l2.printLine(2);
   Line l3 = l1 + l2;
   l3.printLine(3);
   Line l4(7);
   l3.printOtherLine(l4);
   printFriendLine(l4);
   return 0;
}

输出:

Line 1: 10
Line 2: 15
Line 3: 25
Other Line: 7
Friendly Line: 7

在 C++ 中,class X 的所有代码都可以访问 X 的所有部分,无论是哪个对象。

这甚至包括嵌套在 X 中的 class 定义中的代码。

关于我的猜测的基本原理,post-合理化,但访问限制的目的是使 class 更容易正确使用,更难以错误使用,因为其他代码。 class 自己的代码是可信的,无论如何都必须使用所有 class。因此,以任何方式限制 class' 自己的代码都没有多大意义。


关于访问 protected 数据成员有一个有趣的技术问题。在 class Base 中引入的受保护数据成员可以在派生 class Derived 中直接访问,但仅适用于静态已知类型为 Derived 或classes 来自 Derived。这确保您不会无意中访问并使您的代码依赖于其他人的 class' 内部结构,只是通过从公共基础 class.

派生
class Base
{
protected:
    int x_      = 666;
};

class Derived
    : public Base
{
public:
    auto uh_oh( Base const& other ) const
        -> int
    { return x_ * other.x_; }           //← Nyet!
};

auto main()
    -> int
{
    Derived a
    Derived b;
    return a.uh_oh( b );
}

有两种常见的解决方法,其中一种方法确实需要该访问权限。一种是在基class中引入访问函数。另一种是利用成员指针的类型系统漏洞,像这样:

class Base
{
protected:
    int x_      = 666;
};

class Derived
    : public Base
{
public:
    auto uh_oh( Base const& other ) const
        -> int
    { return x_ * other.*&Derived::x_; }        // A somewhat dirty trick.
};

auto main()
    -> int
{
    Derived a;
    Derived b;
    return a.uh_oh( b );
}

这里还有一个隐含的最佳实践问题:当你有一个像getLength这样的访问器,并且还可以访问"the"成员变量length,应该优先使用一个还是另一个?

好吧,可能有人后来想改变实现,例如长度可能以某种方式计算而不是直接存储,如果代码通常使用访问器函数,那么这样做的工作量可能会减少。但另一方面,使用访问器编写代码可能需要更多工作,调试此类代码也可能需要更多工作。所以至少据我所知,在每种情况下都取决于常识,直觉决定。 ;-)