默认赋值运算符可以访问基类的私有成员

ang*_*iac 2 c++

这个例子很容易解释我的问题:

http://pastebin.com/VDBE3miY

class Vector3
{
  float                   _x;
  float                   _y;
  float                   _z;

public :
 /// constructors and stuff

};

class Point : public Vector3
{
// some BS
  Point(float _x):Vector3(float _x)
  {}
};

main()
{
   Point aPoint(3);
   Point anotherPoint(4);

   // WHY DOES THIS WORK and copy _x,_y & _z properly
   aPoint = anotherPoint;
}
Run Code Online (Sandbox Code Playgroud)

基本上,我无法理解为什么=派生类可以复制_x,_y并且_z,即使它不应该访问它们,因为它们是私有的.

jog*_*pan 5

aPoint = anotherPoint;
Run Code Online (Sandbox Code Playgroud)

该行触发Point::operator=(赋值运算符)的调用,该调用存在,因为编译器生成默认实现.此默认实现为类的所有成员执行赋值操作,以及调用Vector3::operator=基类的赋值运算符.反过来,这是一个成员函数,Vector3因此可以访问它复制的所有私有成员.


(编辑)来自C++ 11标准的引用支持这个答案:

(§12.8/ 28)非联合类X的隐式定义的复制/移动赋值运算符执行其子对象的成员复制/移动赋值.首先按照它们在base-specifier-list中的声明顺序分配X的直接基类,然后按照它们在类定义中声明的顺序分配X的直接非静态数据成员. .设x是函数的参数,或者对于move运算符,是指参数的xvalue.每个子对象都以适合其类型的方式分配:

- 如果子对象是类类型,就好像通过调用operator =将子对象作为对象表达式而x的对应子对象作为单个函数参数(就像通过显式限定;即忽略任何可能的虚拟覆盖更多派生类中的函数);

- 如果子对象是一个数组,则以适合于元素类型的方式分配每个元素;

- 如果子对象是标量类型,则使用内置赋值运算符.

一些其他(现在部分删除的)答案提到了由赋值操作执行的按位复制的想法.这有一定的道理:如果您的类或结构定义了POD(普通旧数据)类型,那么它实际上与C结构相同.在这种情况下,可以通过执行复制memcpy,因此可以认为赋值操作看作是基本上等同于一个按位复制.但是,这是一种有效的思考方式的原因是上面的§12.8/ 28,这也适用于非POD类型.

另请注意,从您的代码中可能不一定清楚您的数据类型是POD.您在基类中提到了构造函数和东西:如果这涉及非平凡的复制构造函数,赋值运算符或可能的虚函数,那么您的数据类型不再是POD.


关于注释中的问题:为了从派生类实现中调用基类赋值运算符,只需调用它:

struct Base
{
};

struct Derived : Base
{
  Derived &operator=(const Derived &other)
  { Base::operator=(other); return *this; }
};
Run Code Online (Sandbox Code Playgroud)