C++多态性和切片

3
以下代码会输出:
Derived
Base
Base

但是我需要每个派生对象都放入User :: items中,调用它自己的打印函数,而不是基类的函数。如果不使用指针可以实现吗?如果不可能,我应该如何编写删除User :: items并释放内存的函数,以便不会有任何内存泄漏?

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

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

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

class User{
public:
  vector<Base> items;
  void add_item( Base& item ){
    item.print();
    items.push_back( item );
    items.back().print();
  }
};

void fill_items( User& u ){
  Derived d;
  u.add_item( d );
}

int main(){
  User u;
  fill_items( u );
  u.items[0].print();
}

2
使用智能指针以避免内存泄漏。 - Alex Jasmin
3个回答

5

您需要使用指针,并为基类添加虚析构函数。析构函数不必执行任何操作,但必须存在。您的 add 函数如下:

void add_item( Base * item ){
    item->print();
    items.push_back( item );
}

其中items是一个vector<Base *>。要销毁items(假设有虚析构函数):

for( int i = 0; i < items.size(); i++ ) {
    delete items[i];
}
items.clear();

@Neil 虚析构函数是否只存在于使其成为虚拟的目的?这样它就可以动态销毁基类和派生类对象了吗? - Draco Ater
@Draco 析构函数需要被创建和定义为虚函数,这样当通过基类指针删除对象时,销毁将遵循虚函数查找机制。析构函数自动从子类到基类“链式”调用,但首先必须调用子类的析构函数! - dash-tom-bang
换句话说,对于非虚拟析构函数,“delete items[i];”只会调用基类的析构函数而不是“实际派生”的析构函数。添加“virtual”可以使正确的函数被查找和调用。(顺便说一下,你只需要在基类析构函数上加上虚拟标签,并且你不一定需要为派生类显式定义析构函数。) - dash-tom-bang

1
你需要为基类添加虚析构函数,以确保在调用指向类型为Base的指针的delete时,Derived类型的对象能够被正确销毁。
class Base{
public:
  virtual void print(){ cout << "Base" << endl;}

  virtual ~Base( ) { }  // virtual destructor
};

然后您可以使用Boost的ptr_vector来存储指向对象的指针,当容器被销毁时,这些指针也会被删除。


1
它不能防止内存泄漏,但可以防止未定义的行为。 - anon
好的。关于那个我添加了一个提示。 - Björn Pollex
请看我的第一条评论。未定义行为 != 内存泄漏。此外,建议那些正在学习 C++ 的人使用语义复杂的 Boost 容器并不是很有帮助。为了正确使用这些容器,必须首先理解 C++ 的基本概念。 - anon

0

简单解释:

为了理解发生了什么,您可以尝试定义抽象类Base(例如定义任何方法为纯虚拟方法)。 在这种情况下,我希望您会看到编译器错误。 通过这种方式,您将认识到vector实际上做了什么:当您push_back(派生)时,它通过副本构造函数创建类Base的新实例。 这就是为什么您要使用指针。 然后,vector与您最初创建的Derived类型对象一起工作,而不是自己复制Base类型对象。


是的,我知道。那被称为切片。我只想知道除了使用指针之外还有没有其他的选择机会。 - Draco Ater

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接