2012-01-09 70 views
3

我有一個類A,這是父類B和C. 和A類X,這是一個家長Y和Z.多態性C++,在父類

class A {}; 
class B : public A {}; 
class C : public A {}; 


class X 
{ 
    void foo(A) { std:: cout << "A"; } 
}; 

class Y : public X 
{ 
    void foo(B) {std::cout << "B"; } 
}; 

class Z : public X 
{ 
    void foo(c) {std<<cout <<"C"; } 
}; 

int main() 
{ 
    B b; 
    C c; 

    Y y; 
    Z z; 

    y.foo(b);//prints B // b is a B, and Y::foo takes a B, hence print B 
    y.foo(c);//prints A // mismatch between types, fall back and print A 
    z.foo(b);//prints A // mismatch between types, fall back and print A 
    z.foo(c);//prints C // c is a C, and Y::foo takes a C, hence print C 

    std::vector<A> v; 
    v.push_back(b); 
    v.push_back(c); 

    //In this loop, it always prints A, but *this is what I want to change* 
    for (size_t i = 0; i < v.size(); ++i) 
    { 
     z.foo(v.at(i)); 
     y.foo(v.at(i)); 
    } 
} 
的向量類型的損失

是否可以讓項目打印與硬編碼呼叫相同的結果? 這意味着我會把它們當作它們的原始類型,而不是它的父類型? 或者一旦我把它們放入A的向量中,它們將永遠是A類型的?

+0

'y.foo(C); //打印A'。當然,這不是理想的行爲?我想你想讓它打印「B」?我認爲你需要澄清你想要的行爲。給定'p.foo(q)',是否要打印的文本取決於q的類型,或p的類型還是兩者?考慮所有9個選項'{x,y,z} .foo({a,b,c});'並告訴我們你期望的行爲是什麼。 – 2012-01-09 15:02:59

+0

期望的行爲是第一次打印 含義y.foo(b)應該打印B,並且y.foo(c)應該調用基類x.foo()並打印A – Bg1987 2012-01-09 16:19:55

+0

OK。所以在'p.foo(q)'中:如果q的(動態)類型與'p :: foo'的參數類型相同,那麼應該打印該類型。在所有其他情況下,它應該打印「A」。 – 2012-01-09 16:32:54

回答

6

你所看到的是Object Slicing
您正在將Derived類的對象存儲在一個應該存儲Base類對象的向量中,這會導致Object切片,並且所存儲對象的派生類特定成員被切掉,因此存儲在該向量中的對象只是充當基類的對象。

解決方案:

,可以儲存指針在矢量基類的反對:

vector<X*> 

通過存儲一個指向基類就不會有切片和你能達到預期的多態行爲以及通過使功能virtual
正確的做法是使用合適的代替將原始指針存儲在向量中的合適的Smart pointer。這將確保您不必手動管理內存,RAII將自動爲您做到這一點。

+0

你的意思是,將矢量更改爲矢量? – Bg1987 2012-01-09 14:52:36

+0

我相信你也需要將這些函數虛擬爲@vasile狀態,但是這絕對是切分的。 – greg 2012-01-09 14:55:26

+0

@greg:OP已經聲明函數是'virtual'唯一的問題是Op存儲對象而不是指針。 – 2012-01-09 14:56:38

1

這叫做切片。當你的元素變成std::vector<A>時,它基本上會將elements複製到A的新構建實例中。因此來自派生類的對象的部分將會丟失(「切掉」)。

爲了避免切片你需要使用存儲指針而不是元素的容器,所以你應該使用一個std::vector<A*>,或者如果你的元素堆最好分配在某種智能指針(std::shared_ptrstd::unique_ptr的向量C++ 11,boost::shared_ptrstd::tr1::shared_ptr)。

然而書面您的代碼將無法正常工作,即使你改變: XYZ都以它們的參數按值,而在你的向量的所有元素都會有型A*,所以他們取消引用將產生A,所以它仍然會調用錯誤的方法。這可以通過更改簽名始終以A&A*並使用dynamic_cast嘗試流延入式解決:

class X 
{ 
    void foo(A*) { std:: cout << "A"; } 
}; 

class Y : public X 
{ 
    void foo(A* p) { 
     if (dynamic_cast<B*>(p)) std::cout << "B"; // requires virtual methods in A 
     else     X::foo(p); 
    } 
}; 

class Z : public X 
{ 
    void foo(A*){ 
     if (dynamic_cast<C*>(p)) std::cout << "C"; // requires virtual methods in A 
     else     X::foo(p); 
    } 
}; 

當然dynamic_cast是一個有點昂貴,但如果這是一個問題,你可能要重新考慮你的設計。此外,你還需要確保A, B, C含有一些virtual方法(一virtual destructor將在這裏是一個好主意反正),否則dynamic_cast將無法​​正常工作)

+0

我認爲你已經釘了它,它應該打印「B」或「C」*當且僅當*參數的動態類型和X,Y,Z所需的類型之間匹配。根據OP的評論,我已經編輯了一些問題。 – 2012-01-09 16:44:00