2012-04-29 67 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; 
}