这个问题看起来像Virtual destructor 中的讨论:不动态分配内存时是否需要?
在一个考试问题中,有人问我: - 维护指向动态分配内存的指针的任何基类应该定义什么?
我回答: - 一个复制构造函数和一个赋值运算符(以确保不仅复制指针...... cf deep copy)和一个析构函数(释放分配的内存)
他们说这是不正确的,因为这个基类还应该定义一个虚拟析构函数而不是普通的析构函数。为什么?
这个问题看起来像Virtual destructor 中的讨论:不动态分配内存时是否需要?
在一个考试问题中,有人问我: - 维护指向动态分配内存的指针的任何基类应该定义什么?
我回答: - 一个复制构造函数和一个赋值运算符(以确保不仅复制指针...... cf deep copy)和一个析构函数(释放分配的内存)
他们说这是不正确的,因为这个基类还应该定义一个虚拟析构函数而不是普通的析构函数。为什么?
如果您的类打算以多态方式使用,您可能会有指向派生对象的基类的指针。
virtual
通过指向没有析构函数的基类的指针删除派生对象会导致未定义的行为。这大概就是她的理由。
3)在第一种选择(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义。[...]
如果派生类的对象打算通过基类指针销毁,您的基类需要一个虚拟析构函数,就像这样
Base *pointer_to_base_class = new Derived;
delete pointer_to_base_class;
根据您的问题,尚不清楚是否是这种情况。也许问题的另一部分(或先前的问题)清楚地表明这种多态破坏是有意的。或者,也许您在课堂上被教导要始终将这种使用视为最佳实践。
它们不是 100% 正确的。虚拟析构函数是必须的,如果
否则非虚拟析构函数是可以的。但在大多数情况下,即使只打算使用#1,将析构函数设为虚拟也是一种很好的风格,而不管#2 是什么。
在标准中,大多数继承层次结构在其基础上都有一个虚拟析构函数。但是,sub_match
它被定义为公共继承,std::pair<BidirectionalIterator, BidirectionalIterator>
因此它可以拥有动态分配的内存。在相关领域中,match_results
不要求但通常实现为std::vector<...>
明确分配内存的公共继承。
您的考官并非完全不正确,但对动态分配内存的关注是一种转移注意力的做法,暴露出令人担忧的对标准的无知;虽然在大多数实现中,通过指向没有虚拟析构函数的基类型的指针删除派生类型将导致破坏切片对象,但根据标准,这是未定义的行为。
为了在此处跟进所有好的答案,这是一种良好的做法,即声明一个虚拟析构函数以确保在应该对类进行子类化以形成层次结构并且您希望通过指向它的指针删除派生对象时进行适当的清理。C++ 标准对此很清楚:
当您想通过基类指针删除派生类对象并且基类的析构函数不是虚拟且结果未定义时
通过未定义的行为,您可能会想到内存泄漏,例如,如果您的派生类分配了一些动态内存,并且您稍后尝试通过此基类删除它。您的老师可能正在考虑这种情况。
添加到其他答案:您还可以设想一种情况,您确实需要一个公共基类,但您没有任何实际的接口函数。但是如果你想要 RTTI 和动态转换支持,你需要在你的类中添加一个虚函数。析构函数可以就是那个函数。
例如,假设您是一名正在康复的 Java 程序员,并坚持认为一切都是Object
. 您可能会像这样启动您的第一个 C++ 程序:
class Object
{
public:
virtual ~Object() { }
};
现在Object
确实可以作为每个类的最终多态基类。
如果您还认为这Object
应该是抽象的,您甚至可以将析构函数设为纯虚拟:
class Object { public: virtual ~Object() = 0; }; Object::~Object() { }