有人在IRC中提到它是切片問題。 架構
在我看來,除了您本身的類和程序的架構/設計不良時,切片並非什麼大問題。 函數
若是我將子類對象做爲參數傳遞給採用超類類型參數的方法,則我固然應該意識到這一點並在內部知道,所調用的方法將僅與超類(aka基類)對象一塊兒使用。 性能
在我看來,只有不合理的指望,即在請求基類的狀況下提供子類會以某種方式致使特定於子類的結果,從而致使切片成爲問題。 它要麼使用該方法的設計不佳,要麼使用子類的實現不佳。 我猜測這一般是出於權宜之計或性能提高而犧牲好的OOP設計的結果。 this
好的,在閱讀了不少解釋對象切片的文章以後,我將嘗試一下,但不會出現問題。 spa
可能致使內存損壞的惡劣狀況以下: 設計
class A { int x; }; class B { B( ) : x(1), c('a') { } int x; char c; }; int main( ) { A a; B b; a = b; // b.c == 'a' is "sliced" off return 0; }
這裏的大多數答案都沒法解釋切片的實際問題是什麼。 他們只說明切片的良性案例,而不說明危險的切片。 像其餘答案同樣,假定您正在處理兩個類A
和B
,其中B
(公開地)從A
派生。 code
在這種狀況下,C ++容許您將B
的實例傳遞給A
的賦值運算符(以及傳遞給副本構造函數)。 之因此可行,是由於B
的實例能夠轉換爲const A&
,這是賦值運算符和複製構造函數所指望的參數。 對象
B b; A a = b;
那裏什麼都沒發生-您要求A
的實例,它是B
的副本,而這正是您所獲得的。 固然, a
不會包含b
的某些成員,可是應該如何? 畢竟是A
,而不是B
,因此它甚至都沒有據說過這些成員,更不用說可以存儲它們了。 繼承
B b1; B b2; A& a_ref = b2; a_ref = b1; //b2 now contains a mixture of b1 and b2!
您可能會認爲b2
將是b1
的副本。 可是,惋惜不是 ! 若是對其進行檢查,您會發現b2
是弗蘭肯斯坦主義的生物,它由b1
一些塊( B
繼承自A
的塊)和b2
一些塊(僅B
包含的塊)組成。 哎喲! 內存
發生了什麼? 好吧,默認狀況下,C ++不會將賦值運算符視爲virtual
。 所以,行a_ref = b1
將調用A
的賦值運算符,而不是B
的賦值運算符。 這是由於,對於非虛函數, 聲明的類型(即A&
)肯定要調用哪一個函數,而不是實際的類型(即B
,由於a_ref
引用B
的實例)。 如今, A
的賦值運算符顯然只知道A
聲明的成員,所以它將僅複製那些成員,而保持B
添加的成員不變。
一般只分配對象的一部分幾乎沒有什麼意義,可是不幸的是,C ++沒有提供內置的方式來禁止這種狀況。 可是,您能夠本身滾動。 第一步是使賦值運算符爲virtual 。 這樣能夠確保始終調用的是實際類型的賦值運算符,而不是聲明的類型的賦值運算符。 第二步是使用dynamic_cast
驗證分配的對象是否具備兼容的類型。 第三步是在(受保護的!)成員assign()
進行實際分配,由於B
的assign()
可能要使用A
的assign()
複製A
的成員。
class A { public: virtual A& operator= (const A& a) { assign(a); return *this; } protected: void assign(const A& a) { // copy members of A from a to this } }; class B : public A { public: virtual B& operator= (const A& a) { if (const B* b = dynamic_cast<const B*>(&a)) assign(*b); else throw bad_assignment(); return *this; } protected: void assign(const B& b) { A::assign(b); // Let A's assign() copy members of A from b to this // copy members of B from b to this } };
請注意,爲純粹方便起見, B
的operator=
協變量會覆蓋返回類型,由於它知道它正在返回B
的實例。
切片意味着當子類的對象經過值或從指望基類對象的函數傳遞或返回時,子類添加的數據將被丟棄。
說明:考慮如下類聲明:
class baseclass { ... baseclass & operator =(const baseclass&); baseclass(const baseclass&); } void function( ) { baseclass obj1=m; obj1=m; }
因爲基類複製函數對派生類一無所知,所以僅複製了派生類的基礎部分。 這一般稱爲切片。