2012-11-28 39 views
1

是否存在C++對象切片效果的任何示例,這些示例可能導致未定義的行爲,內存泄漏或其他正確的代碼集崩潰?例如,當類AB(從A繼承)是正確和可靠的,但調用void f(A a)明顯會導致令人討厭的事情。導致泄漏/未定義的行爲/崩潰的C++切片

它是需要形成一個測試問題。目標是要知道參與者是否意識到切片現象,使用一個正確性不應該成爲意見的示例代碼片段。

回答

3

如果A確實是「正確無誤的」,那麼切片(複製基礎子對象)定義良好,不會導致您提到的任何問題。如果您希望副本的行爲類似B,則它會導致的唯一問題是意外行爲。

如果A不能正確複製,則切片將導致複製該類型對象時出現的任何問題。例如,如果它有一個析構函數,用於刪除由對象持有的指針,並且複製創建了一個指向同一事物的新指針,那麼當兩個析構函數刪除相同的指針時,您將獲得未定義的行爲。這不是切片本身的問題,而是切片對象的無效複製語義。

+0

好的。那麼有關測試某人意識的任何提示? – Notinlist

1

你總是可以構建這樣一個例子

struct A { 
    A() : invariant(true) {} 
    virtual void do_sth() { assert(invariant); } 
protected: 
    bool invariant; 
}; 

struct B : A { 
    B() { invariant=false; } 
    virtual void do_sth() { } 
}; 

void f(A a) 
{ 
    a.do_sth(); 
} 

當然,這可以防止內部A,當拷貝構造函數/賦值運算符不檢查是否不變的是真實的。

如果不變量比我的布爾值更隱含,這些東西可能非常棘手。

+1

我在這裏看不到任何未定義的行爲 – 2012-11-28 13:39:23

+0

* 1 *這不是對象切片,因爲'B'沒有新的數據被切掉。* 2 *除非你用'B'參數調用'f(A)',否則我看不出你的代碼有什麼問題。 – Walter

+1

@Walter:用'B'參數調用'f(A)'正是問題出現的地方:'B'被切片爲'A',然後失敗了'A :: do_sth'中的斷言。 –

1

如果您通過指針或對其基類的引用來操作派生類,則對象切片實際上只是一個問題。然後,派生類的附加數據保持不變,而基本部分的附加數據可能會被修改。這可能會破壞派生類的不變量。舉個簡單的例子,參見http://en.wikipedia.org/wiki/Object_slicing

但是,我會認爲這是一個設計缺陷(派生類)。如果通過任何合法的方法來訪問一個類,包括那些作爲基類的指針或引用參數,都可以破壞它的不變量,那麼這個類就會被設計的很糟糕。避免這種情況的一種方法是聲明基地private,以便派生類不能通過其基地合法訪問。

一個例子是:

class A 
{ 
    int X; 
    A(int x) : X(x) {} 
    void doubleX() { X+=X; } 
    /* ... */ 
}; 

class B : public A 
{ 
    int X_square; 
    B(int x) : A(x), X_square(x*x) {} 
    /* ... */  
}; 

B b(3); 
B.doubleX(); /// B.X = 6 but B.X_square=9 

從這個例子也很明顯,這是B.簡單的設計缺陷,在這個例子中,提供

void B::doubleX() { A::doubleX(); X_squared=X*X; } 

不能解決問題,如

A&a=b; 
a.doubleX(); 

仍然打破不變。這裏唯一的解決方案是宣佈基地A私人或更好地使A私人成員,而不是基地B