2012-04-29 64 views
1

我有兩個類像這樣(簡體和重命名):克++忽略覆蓋虛函數

class Base 
{ 
public: 
    virtual void operator()() { cout << "Base\n";} 
}; 

class Derived : public Base 
{ 
public: 
    void operator()() { cout << "Derived\n"; } 
}; 

void doSomething(Base obj) 
{ 
    obj(); 
} 

int main() 
{ 
    list<Base> bList; 
    Derived d1, d2, d3; 
    bList.push_back(d1); 
    bList.push_back(d2); 
    bList.push_back(d3); 

    for(list<Base>::iterator it = bList.begin(); it != bList.end(); it++) 
    { 
     doSomething(*it); 
    } 
    return 0; 
} 

當我跑,我得到:

Base 
Base 
Base 

即基本::運算符()叫做。顯然這不是我想要發生的事情。 我試圖改變DoSomething的到

void doSomething(Base& obj) 

但這只是如果與派生類對象,而不是一個從列表中直接調用工作。並且引用不能存儲在列表中。
有什麼辦法可以讓doSomething「看到」它必須調用派生類的函數(而不必訴諸指針)?

編輯:問題指出,對於誰可能會遇到這種這裏其他人是一個鏈接: What is object slicing?

+0

使用指向'Base'的指針,並檢查是否可以'dynamic_cast'到'Derived'。 – chris 2012-04-29 14:49:35

+0

@chris:這是錯誤的一半。建議使用指針是好的,但使用'dynamic_cast'的建議不是。沒有必要施放,虛擬調度機制將選擇正確的覆蓋。 – 2012-04-29 15:05:19

+0

http:// stackoverflow。com/questions/274626/what-is-slicing-problem-in-c – Flexo 2012-04-29 15:06:21

回答

2

問題不在於編譯器沒有「看到」它需要調用派生類中的函數,問題是您只將基類存儲在容器中list<Base>。這導致了通常稱爲對象切片的情況。當編譯器將您的對象複製到只能容納對象的容器中時,它只能複製對象的基礎部分,並將對象的「切片」複製到零件上。

爲了在容器遵循值語義的情況下使用多態(例如,假設您將實際對象存儲在它們中,而不是對對象的引用形式),您需要存儲指向所述對象的指針。我建議不要將原始指針存儲在標準庫容器中,因爲它會引入額外的生命週期管理問題 - 您必須從容器外部控制指向對象的生存期,這往往是一個非常糟糕的想法,通常是導致內存泄漏或懸掛指針,如果不是兩者。

我的建議是,如果你可以使用boost,使用明確爲此設計的ptr_container模板類之一。如果你不能,而且你使用的是足夠現代的編譯器,則可以使用std::list<std::shared_ptr<Base> > >並依靠shared_ptr來管理對象的生命週期。

+0

在C++ 11中,如果不需要共享,他可以使用'std :: unique_ptr '。僅使用一個主題管理引用計數會更有效。 – 2012-04-29 17:02:13

7

以下:

list<Base> bList; 
Derived d1, d2, d3; 
bList.push_back(d1); 
bList.push_back(d2); 
bList.push_back(d3); 

你的對象。他們不再是Derived類型,而是Base。您需要在容器中使用指針或智能指針以獲得預期的行爲。

閱讀對象切片。

+1

**和**修復'doSomething'功能,以避免切片 – juanchopanza 2012-04-29 14:53:30

+0

謝謝,現在我明白了爲什麼會發生。 – 2012-04-29 14:56:16

0

上面的答案讓我想到了最終的解決方案。簡短的回答是,「不,你必須使用指針列表。」這是我想出來的...

#include <iostream> 
#include <memory> 
#include <list> 

class Base 
{ 
    public: 
     virtual void foo() 
     { 
      std::cout << "Base::foo" << std::endl; 
     } 
}; 

class Derived : public Base 
{ 
    public: 
     void foo() 
     { 
      std::cout << "Derived::foo" << std::endl; 
      Base::foo(); 
     } 
}; 

int main() 
{ 
    std::list<std::shared_ptr<Base>> bases; 
    auto p = std::make_shared<Derived>(); 
    bases.push_back(p); 

    for (std::list<std::shared_ptr<Base>>::iterator i = bases.begin(); i != bases.end(); i++) 
    { 
     (*i)->foo(); 
    } 

    return 0; 
}