我之前见过这个问题,但不清楚或与我遇到的情况相同。
我有一个抽象的基类。它有一个受保护的构造函数和一个析构函数。它由几个完整的类型继承,它们也有公共构造函数和析构函数。我遇到的问题是,如果对象被基类型引用,则删除对象不会调用子析构函数。
class Tree
{
protected:
Tree(){ }
public:
~Tree(){ }
};
class OakTree : public Tree
{
public:
OakTree(){ }
~OakTree(){ }
};
vector<Tree*> Trees; // Store objects using the base type
Trees.push_back(new OakTree()); // Create derived object
delete Trees[0]; // OakTree desctructor does not get called
如何调用OakTree析构函数?我已经尝试将所有析构函数标记为虚拟但这不起作用。基类析构函数不能是抽象的(这可以解决调用问题而不是删除问题)。
答案 0 :(得分:7)
制作基类析构函数virtual
。
class Tree
{
protected:
Tree(){ }
public:
virtual ~Tree(){ }
}
否则,如果尝试通过基类指针删除,将导致未定义的行为。这有点过时了,但Scott Meyers在 Effective C ++ 中表达了这一点,第二版:
C ++语言标准在这个主题上非常清楚:当您尝试通过基类指针删除派生类对象并且基类具有非虚拟析构函数时(
EnemyTarget
确实如此),结果是不确定的。这意味着编译器可以生成代码来执行他们喜欢的任务:重新格式化磁盘,向老板发送暗示性邮件,将源代码传真给竞争对手,等等。 (在运行时经常发生的事情是派生类的destrutcor永远不会被调用。...)
答案 1 :(得分:3)
这是因为您的基类析构函数未声明为虚拟。
class Tree
{
protected:
Tree(){ }
public:
virtual ~Tree(){ }
}
答案 2 :(得分:0)
如果要使用多态,则必须在基类中使用虚拟析构函数。
问题是当你在Tree *
上调用delete时,没有人真正知道它是什么类型的树,并且通过使用虚拟析构函数,编译器通过指向表的函数生成一个调用那个特定的对象实例,你可以获得任何类型的析构函数。
否则标准说它是未定义的行为,但大多数时候似乎发生的事情是编译器生成对析构函数的调用,无论你的多态指针或引用是什么类型,在你的情况下,它是基类。
答案 3 :(得分:0)
我想知道为什么没有人真正询问作者试图通过代码实现什么目标?
虽然,是的,在基类中有一个虚拟析构函数可以帮助你调用你的析构函数,你还剩下的是未定义的行为。
您对delete Trees[0];
行后的期望是什么?
或者说,你想用这条线来实现什么目标?
如果您尝试从矢量中删除项目,则可能需要:
Trees.erase(Trees.begin());
但是,如果你忘记删除行,你就会留下内存泄漏。
因此,不要在向量中使用普通指针。考虑:
std::vector< std::shared_ptr<Tree> > Trees;
Trees.push_back( std::make_shared<OakTree>() );
Trees.erase(Trees.begin());
Runnable @Ideone