2012-11-26 107 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'。但我已經爲你簡化了它。關鍵是編譯器會自動生成這些方法(並且它們可以工作(以淺拷貝的方式;這在大多數情況下都是你想要的)(在指針不關心的地方)))。 –