这个例子很容易解释我的问题:
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;
}
基本上,我无法理解为什么派生类的=
可以复制_x
,_y
和_z
,即使它不应该有因为他们是私人的,所以可以访问它们。
答案 0 :(得分:5)
aPoint = anotherPoint;
此行触发Point::operator=
(赋值运算符)的调用,该调用存在,因为编译器生成默认实现。此默认实现为类的所有成员执行赋值操作,以及调用基类的赋值运算符Vector3::operator=
。反过来,这是Vector3
的成员函数,因此可以访问它复制的所有私人成员。
(EDIT)来自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; }
};
答案 1 :(得分:0)
因为调用了Vector3的默认复制操作符(浅拷贝)。
答案 2 :(得分:0)
因为编译器生成赋值运算符
Point& operator=(Point const& rhs)
{
Vector3::operator=(rhs);
return *this;
}
Vector3& operator=(Vector3 const& rhs)
{
// A class is a friend of irself.
// So an object of Type A can read any other object of type A
_x = rhs._x;
_y = rhs._y;
_z = rhs._z;
return *this;
}