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

即 Base::operator() 被调用。显然这不是我想要发生的。我尝试将 doSomething 更改为

void doSomething( Base& obj )

但这仅在直接使用 Derived 对象而不是列表中的对象调用时才有效。并且引用不能存储在列表中。
有什么办法可以让 doSomething “看到”它必须调用派生类的函数(不必求助于指针)?

编辑:指出了问题,对于可能遇到此问题的其他人来说,这里有一个链接: 什么是对象切片?

4

3 回答 3

7

以下:

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

切片你的对象。它们不再是类型Derived,而是Base。您需要在容器中使用指针或智能指针来实现预期的行为。

阅读对象切片。

于 2012-04-29T14:51:19.313 回答
2

问题不在于编译器没有“看到”它需要调用派生类中的函数,问题在于您只是将基类存储在您的容器中list<Base>。这导致了通常所说的对象切片。当编译器将您的Derived对象复制到只能容纳Base对象的容器中时,它只能复制对象的基本部分并“切片”使其成为Derived对象的部分。

为了在遵循值语义的容器中使用多态性(即,假设您将实际对象存储在其中,而不是对对象的引用形式),您需要存储指向所述对象的指针。我建议不要将原始指针存储在标准库容器中,因为它会引入额外的生命周期管理问题——您必须从容器外部控制指向对象的生命周期,这往往是一个非常糟糕的主意,通常要么导致内存泄漏或悬空指针,如果不是两者兼而有之。

我的建议是,如果您可以使用 boost,请使用ptr_container为此目的明确设计的模板类之一。如果不能,并且您使用的是足够现代的编译器,则可以使用 astd::list<std::shared_ptr<Base> > >并依靠shared_ptr来管理对象的生命周期。

于 2012-04-29T14:59:52.290 回答
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;
}
于 2013-01-26T06:36:08.870 回答