我最近被问到一个基本的C ++问题,我实际上并不知道答案,我从来没有发现答案是什么。这是一个问题:
运行此程序时可能会导致错误。您将对Base类进行哪些更改来修复它?
#include <iostream>
class Base {
public:
virtual void ShowMessage () {
std::cout << "Base class message\n";
}
// Something should be added here!
};
class Derived : public Base {
public:
explicit Derived (const std::string & value)
: myValue(value)
{
}
virtual void ShowMessage () {
std::cout << "Derived class message " << myValue << "\n";
}
private:
std::string myValue;
};
int main () {
Base * obj = new Derived("Test message");
obj->ShowMessage();
delete obj;
}
它在我的机器上编译并运行得很好。有人知道他们在找什么吗?
答案 0 :(得分:7)
您需要向Base类添加虚拟析构函数。
class Base {
public:
virtual void ShowMessage () {
std::cout << "Base class message\n";
}
virtual ~Base() {}
};
在您的情况下,Derived :: myvalue将无法正确发布。
答案 1 :(得分:4)
虚拟析构函数。当这段代码执行时......
delete obj;
...除非以多态方式(即通过虚拟调度)调用析构函数,否则不会知道有std::string
来解析/解除分配。那是因为没有虚拟调度,Base类析构函数会运行,并且它不会让std::string
数据成员担心,因此不包含与之相关的代码。
指南很简单:无论何时使用指向基类的指针删除对象,都应确保基类具有虚拟析构函数。
其他任何问题都存在上述问题,并且在技术上是未定义的行为。虽然尝试“推理”未定义的行为并不是一个好主意,因为你不能确定发生的事情会不会比预期更糟(如果不是现在,那么在某些未来的编译器/编译器版本,或者使用不同的编译在这种情况下,内存泄漏是一个明显可能的后果:std :: string构造函数可能需要动态new
- 编写一个字符数组,并且教师会删除它如果运行。仍然存在其他问题 - 如果派生类的数据成员说共享指针,那么如果存在文件句柄,共享内存句柄,线程,锁等等,则指向对象可能无法正确销毁/解除分配。无法调用析构函数的后果可能非常严重:数据可能无法刷新到预期目标,应用程序可能会在重复泄漏后耗尽资源,或者当其他一些代码尝试获取它仍然持有的锁时可能会挂起。 ...
答案 2 :(得分:1)
您必须使析构函数为虚拟,因为删除不带虚拟析构函数的多态类型会导致未定义的行为:
按5.3.5.3:
在第一个替代(删除对象)中,如果是静态类型的 要删除的对象不同于其动态类型,静态 type应该是对象的动态类型的基类 删除和静态类型应具有虚拟析构函数或 行为未定义。在第二个替代(删除数组)如果 要删除的对象的动态类型与其静态类型不同, 行为未定义。