如果在派生类中定义了虚拟析构函数,但不是层次结构的顶部,该怎么办?C++

What if the virtual destructor defined in derived class, but not the top of hierarchy? C++

本文关键字:层次结构 顶部 C++ 怎么办 派生 定义 如果 析构函数 虚拟      更新时间:2023-10-16

我想知道定义一个没有虚拟析构函数的基类,并使用虚拟析构函数定义继承的类是否正确?如果我这样做,实际上会发生什么?

如果你delete p pX*类型,但实际上指向一个派生自XY,除非X有一个virtual析构函数,否则你有未定义的行为。如果Y的析构函数是virtualX析构函数不是,它什么都不会改变。

如果你打算这样做

delete pB;

其中pB是类型Base*pB可能指向类型Derived的对象,其中Derived是(直接或间接(派生的Base类,那么

必须为 Base 声明虚拟析构函数。

只要您不打算这样做,Base就可以没有虚拟析构函数。

特别是,如果您有具有非虚拟析构函数的类B1,以及从具有虚拟析构函数的B1派生的类B2,以及派生自B2的类D,则可以使用指向B2的指针来删除类型D的对象,因为B2具有虚拟析构函数...但是使用指向B1的指针来删除类型为 B2D 的对象是未定义的行为,因为B1没有虚拟析构函数。

无论如何,函数在派生类中virtual,而在基类中不virtual的想法是令人困惑的,所以除非我有很好的理由,否则我不会这样做。

虚拟析构函数允许您仅使用指向基类的指针来调用正确的析构函数。如果没有虚拟析构函数,delete base_ptr只会调用基类的析构函数(或调用未定义的行为(。但是,析构函数调用是否动态调度仅取决于基类,因此,如果基类不使其成为虚拟的,那么如果某些派生类添加了虚拟析构函数,这不会突然变成虚拟调度!

笔记:

    如果基类
  • 没有虚拟析构函数,请不要将其用作基类。最多使用私有/受保护的继承,但不使用公共继承。
  • 如果您编写基类但不想要求虚拟析构函数(在编写 mixins 时很常见(,则可以改为保护析构函数。

这两个建议可防止意外调用非虚拟基类析构函数。

如果您不在父类中声明 virtual,它将不起作用。

[~/cpp/VirtualDestructor]$ cat main.cpp
class A
{
public:
    ~A() {}
};
class B : public A
{
public:
    B() : val(new char) {}
    virtual ~B() { delete val; }
private:
    char* val;
};
int main()
{
    A* x = new B;
    delete x;
}
[~/cpp/VirtualDestructor]$ valgrind ./run 
==20382== Memcheck, a memory error detector
==20382== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==20382== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==20382== Command: ./run
==20382== 
==20382== 
==20382== HEAP SUMMARY:
==20382==     in use at exit: 1 bytes in 1 blocks
==20382==   total heap usage: 2 allocs, 1 frees, 17 bytes allocated
==20382== 
==20382== LEAK SUMMARY:
==20382==    definitely lost: 1 bytes in 1 blocks
==20382==    indirectly lost: 0 bytes in 0 blocks
==20382==      possibly lost: 0 bytes in 0 blocks
==20382==    still reachable: 0 bytes in 0 blocks
==20382==         suppressed: 0 bytes in 0 blocks
==20382== Rerun with --leak-check=full to see details of leaked memory
==20382== 
==20382== For counts of detected and suppressed errors, rerun with: -v
==20382== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
[~/cpp/VirtualDestructor]$

B实例由A*指针指向。然后当你delete它时。如果A的析构函数不是virtual,它将通过A的析构函数