2013-06-12 104 views
0

我有這兩個類:vector.push_back(B)和vector.push_back(new A((* B))),爲什麼行爲不一樣?

class A 
{ 
public: 
    A(); 
    virtual ~A(); 

    virtual void TellMyName(); 
}; 

class B : public A 
{ 
private: 
    std::string szName; 
public: 
    B(); 
    ~B(); 

    void TellMyName(); 

    void SetName(std::string val){ szName = val; } 
}; 

這是我的代碼:

void main() 
{ 
    std::vector<A*> List_A; 

    B* B_Instance = new B(); 
    B_Instance->SetName("B"); 

    List_A.push_back(B_Instance); // Way 1 
    List_A.push_back(new A((*B_Instance))); // Way 2 

    List_A[0]->TellMyName(); 
    List_A[1]->TellMyName(); 
} 

TellMyName()只是要提示消息框。如果我使用「方式1」,它沒有問題,但如果我使用「方式2」,它會提示沒有文本的消息,這意味着B類的所有成員都是空的,就像他們從未被任何東西填充。我通過使用std::shared_ptr解決了這個問題,但有沒有其他方法可以不使用智能指針,因爲我必須在一個大項目中實現這個方法,並且會有很多變化和失敗。順便說一下,「方式2」的原因是什麼?

+3

閱讀*對象切片*。 – juanchopanza

+2

http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c –

+0

謝謝!我並沒有真正瞭解切片的事情。 – MahanGM

回答

6
List_A.push_back(new A((*B_Instance))); // Way 2 

這條線沒有做你認爲的事。

您正在通過調用由C++生成的默認拷貝構造函數在堆上創建類「A」的10新實例。該拷貝構造函數使用B的實例(將它視爲A的一個實例)並執行逐個字段的拷貝,在這種情況下,它不做任何事情。

你基本上是創建一個A的空實例並推送到列表中。

正如許多人所說的,正式的術語叫做object slicing

2

這意味着B級的所有成員都像他們從來不填充任何東西

號這並不意味着它們是空是空的。要成爲空洞的人,他們首先必須在那裏。

List_A[1]->TellMyName();執行動態調度並最終調用A::TellMyName,因爲class A是對象的實際類型。 B類的成員甚至不存在。

您已經遇到切片問題。

2
std::vector<A*> List_A; 
// ... 
List_A.push_back(new A((*B_Instance))); // Way 2 

您患有某種形式的Object Slicing,有點。

當您使用派生類型的值實例化(或分配)基類時,會發生對象切片,並且會丟失 - 或「切片」 - 派生類所獨有的所有內容。

上面,你正在實例化一個new A,而不是一個new B。但由於BA衍生,你可以簡單地實例化一個new B和返回的指針強制轉換爲A*

List_A.push_back(new B(*B_Instance)); // Way 2 

的這一切都這樣說,作爲一個在旁邊我會強烈勸你考慮重構你的代碼使用智能指針。

2

你應該叫

List_A.push_back(new B((*B_Instance))); 

由於從AB繼承它也可以被添加到列表中,但作爲B拷貝構造函數也將在B複製所有領域,它會工作。

調用A的複製構造函數不復制B中的字段,因爲A不知道B的字段。

相關問題