在 4 级继承链中C++虚拟析构函数
C++ Virtual Destructors in a 4 level inheritance chain.
我正在用虚拟析构函数做一个小实验来审查 - 想知道是否有人对以下内容有一个简单的解释(使用 vs 2010):
我定义类层次结构A-B-C-D,D继承C,C继承B,B继承A,A是基;
运行了 2 个实验:
第一个实验——
A 有一个虚拟析构函数。
B 具有非虚拟析构函数
C 有一个虚拟析构函数
D 具有非虚拟析构函数
//----------------------------
在 D 类型的堆上分配 4 个对象 - 将A*、B* 和 C* 的指针指向前 3 个 - 为了完整起见,将第 4 个对象保留为 D*。删除所有 4 个指针。
正如我所料,在所有 4 个实例中,完整的析构函数链以从 D 到 A 的相反顺序执行,释放所有内存。
第二个实验 -
A 具有非虚拟析构函数** 将 A 更改为非虚拟析构函数
B 具有非虚拟析构函数
C 有一个虚拟析构函数
D 有一个非虚拟析构函数
在 D 类型的堆上分配 4 个对象 - 将 A*、B* 和 C* 的指针指向前 3 个 - 将第 4 个对象保留为 D* 以确保完整性。
删除 C* 和 D* 指针:完整的析构函数链以从 D 到 A 的相反顺序执行,释放所有内存。
删除 B*:B 然后运行析构函数(泄漏)
删除 A*:仅运行析构函数(泄漏)
谁能解释为什么会这样?
当在实验 2 中分配 D 类型反对项时,它的直接基类 (C) 有一个虚拟析构函数 - 这难道不是告诉编译器使用 Vptr 跟踪它并知道内存类型吗?不管参考?
谢谢话筒
当在实验 2 中分配 D 类型反对项时,它的直接基类 (C) 有一个虚拟析构函数 - 这难道不是告诉编译器使用 Vptr 跟踪它并知道内存类型吗?不管参考?
不。
在第二个测试用例中,A
和B
没有 vptrs/vtables。 (即使他们这样做了,非虚拟成员函数仍将静态解析,而不是动态解析。
换句话说,基类不会从派生类"继承"信息(例如函数是否为虚拟函数)。
删除没有虚拟析构函数的 A* 时,编译器在编译时不知道它将在运行时指向具有虚拟析构函数的对象。删除可能是具有虚拟析构函数的对象 - 也可能不是。不会发生动态绑定。
您的实际问题是为什么使用虚拟析构函数与非虚拟析构函数?Cos 具有非虚拟析构函数的基类是不好的。查看常见问题解答
我写了几乎相同的问题,所以我想分享一下。
请注意,我还在不同的 Ctor中添加了一些虚函数的用法,以说明它是如何工作的(很快,在每个 Ctor 中,V-table 只更新"直到它",这意味着将被调用的虚函数实现是继承链的"这一点"之前派生最多的)。
我的一个说明:在运行给定类的示例代码中,我还在 STACK 上添加了"派生"对象(B 和 D)的创建 -->,以强调当我们使用指向类实例的指针(任何类型的)时,所有关于 Dtor"虚拟性"的考虑都是适用的。
class A;
void callBack(A const& a);
class A
{
public:
A() { std::cout << "A Ctor " << std::endl; f1(); callBack(*this); /* f3();*/ }
~A() { std::cout << "A Dtor " << std::endl; }
void f1() { std::cout << "A : f1 " << std::endl; }
virtual void f2() const { std::cout << "A : f2 " << std::endl; }
virtual void f3() = 0;
};
class B : public A
{
public:
B() { std::cout << "B Ctor " << std::endl; f1(); callBack(*this); f3(); }
~B() { std::cout << "B Dtor " << std::endl; }
void f1 () { std::cout << "B : f1 " << std::endl;}
void f2() const { std::cout << "B : f2 " << std::endl; }
virtual void f3() { std::cout << "B : f3 " << std::endl; }
};
class C : public A
{
public:
C() { std::cout << "C Ctor " << std::endl; f1(); callBack(*this); f3(); }
virtual ~C() { std::cout << "C Dtor " << std::endl; }
void f1() { std::cout << "C : f1" << std::endl;}
void f2() const { std::cout << "C : f2" << std::endl; }
virtual void f3() const { std::cout << "C : f3" << std::endl; }
};
class D : public C
{
public:
D() { std::cout << "D Ctor " << std::endl; f1(); callBack(*this); }
~D() { std::cout << "D Dtor " << std::endl; }
void f1() { std::cout << "D : f1" << std::endl; }
void f2() const { std::cout << "D : f2 " << std::endl; }
virtual void f3() { std::cout << "D : f3 " << std::endl; }
};
void callBack(A const& a) { a.f2(); }
// =================================================================================================================================
int main()
{
std::cout << "Start of main program" << std::endl;
std::cout << "Creating a D object on the heap" << std::endl;
D* pd = new D;
C* pc = new D;
A* pa = new D;
if (true)
{
std::cout << "Entering Dummy scope # 1 and creating B object on the stack" << std::endl;
B b;
std::cout << "Leaving Dummy scope # 1 with B object within it" << std::endl;
}
if (true)
{
std::cout << "Entering Dummy scope # 2 and creating D object on the stack" << std::endl;
D d;
std::cout << "Leaving Dummy scope # 2 with D object within it" << std::endl;
}
std::cout << "Calling delete on pd (D*) which points on a D object" << std::endl;
delete pd;
std::cout << "Calling delete on pc (C*) which points on a D object" << std::endl;
delete pc;
std::cout << "Calling delete on pa (A*) which points on a D object" << std::endl;
delete pa;
std::cout << "End of main program" << std::endl;
return 0;
}
- 虚拟决赛作为安全
- PowerPC ppc64le上的Gcc Woverloaded虚拟错误
- 如何在C++中获得"静态纯虚拟"功能?
- C++无法定义虚拟函数 OUTER 类和头文件
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 大小虚拟继承中的派生类
- 链接器找不到在虚拟类 c++ 中访问的静态字段的符号
- 使用 C++ 和 i2c 工具从虚拟 i2c 写入和读取
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 如果整个应用程序是虚拟映射的,为什么 new 会进行系统调用?
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- std::is_trivially_copyable_v 关于虚拟功能
- 删除C++继承中虚拟类成员的代码重复
- 子类地址等于虚拟基类地址?
- 当覆盖存在时调用基本虚拟"binded to object"函数
- 用于创建/注册虚拟存储设备的 IOKit 驱动程序
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?