2012-11-26 66 views
2

我的问题是很容易通过这个例子来说明:默认赋值运算符访问基类的私有成员

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; 
} 

基本上,我茫然地理解为什么=为派生类可以复制_x,_y_z,即使它们不应该访问它们,因为它们是私有的。

+0

“It”should not have access?谁不应该访问? – jogojapan

+0

“默认=为派生类” – angryInsomniac

+0

你知道这不是有效的C++,对吧? –

回答

0

因为Vector3的默认复制操作符被调用(浅拷贝)。

+1

什么是复制操作符?有一个拷贝构造函数和一个拷贝赋值操作符。哪一个? –

4
aPoint = anotherPoint; 

此行触发Point::operator=呼叫(赋值运算符),它的存在是因为编译器生成的默认实现。这个默认的实现为类的所有成员执行赋值操作,并且调用基类的赋值操作符Vector3::operator=。这又是Vector3的成员函数,因此可以访问它复制的所有私人成员。


(编辑)从C++ 11标准A报价来支持这一答案:

(§12.8/ 28)对于非联合的隐式定义的复制/移动赋值运算符类X执行其子对象的成员复制/移动分配。首先分配X的直接基类,按照它们在base-specifier-list中的声明顺序,然后按照它们在类定义中声明的顺序分配X的直接非静态数据成员。设x是该函数的参数,或者对于移动运算符,是指参数的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; } 
}; 
+0

好吧,所以说我想重载Point的赋值操作符,做一些特定的成员变量的深层副本,有没有办法从里面调用Vector3 :: operator =? 或者我只需要让这些成员受到保护,然后手动复制它们。 – angryInsomniac

+0

此外,你有什么要说的这个: http://stackoverflow.com/questions/13559255/default-assignment-operator-has-access-to-private-members-of-base-class/13559416# 13559416 – angryInsomniac

+1

@angryInsomniac我认为我的回答错了,所以我删除了它。这个看起来对我来说是正确的。顺便说一句,要调用'Vector3 :: operator =()',你需要:'aPoint.Vector3 :: operator =(anotherPoint)'。 –

0

因为编译器生成赋值运算符( s)

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; 
} 
+0

如果Point在它的定义中添加了一些成员,它会在我的实现中执行,但是imho不起作用。 – angryInsomniac

+0

有关编译器生成的方法,请参阅:http://stackoverflow.com/a/1810320/14065。 –

+0

PS:演员阵容可以作为'Point'是'Vector3'。但我已经为你简化了它。关键是编译器会自动生成这些方法(并且它们可以工作(以浅拷贝的方式;这在大多数情况下都是你想要的)(在指针不关心的地方)))。 –