gpt4 book ai didi

c++ - 我怎样才能理解这些析构函数?

转载 作者:太空宇宙 更新时间:2023-11-03 10:41:52 24 4
gpt4 key购买 nike

我对以下 C++ 代码感到困惑(在 http://cpp.sh/8bmp 在线运行它)。它结合了我在类(class)中学习的几个概念。

#include <iostream>
using namespace std;

class A {
public:
A() {cout << "A ctor" << endl;}
virtual ~A() {cout << "A dtor" << endl;}
};

class B: public A {
public:
B() {cout << "B ctor" << endl;}
~B() {cout << "B dtor" << endl;}
void foo(){cout << "foo" << endl;}
};

int main(){
B *b = new B[1];
b->~B();
b->foo();
delete b;
return 0;
}

输出:

A ctor
B ctor
B dtor
A dtor
foo
A dtor

这是我不明白的地方:

  1. 为什么调用析构函数后还能调用foo
  2. 为什么调用析构函数后可以调用delete
  3. 如果我注释掉 delete b; 这段代码会不会泄漏内存?
  4. A 的析构函数是虚拟的。我认为子类中重载的虚函数不会被调用。为什么 ~A() 会被调用?
  5. 如果我注释掉 b->~B();,则 B dtor 行将打印在 foo 之后。为什么?
  6. 如果我重复 b->~B(); 行两次,则输出为:B dtor\nA dtor\nA dtor。嗯?
  7. 如果将 delete B; 切换为 delete[] b;,我会得到相同的输出。我认为第二个是正确的,因为 b 是用 new[] 创建的,但这并不重要,因为我只推送一个 B 到堆。对吗?

很抱歉问了这么多问题,但这让我很困惑。如果我的个别问题被误导了,请告诉我我需要了解什么才能了解​​每个析构函数何时运行。

最佳答案

“未定义行为”(简称 UB)是允许编译器做任何事情的地方——这通常意味着介于“崩溃”、“给出不正确的结果”和“无论如何做你期望的事情”之间的某个地方。你的 b->foo() 绝对是未定义的,因为它发生在你的 b->~B() 调用之后,

由于您的 foo 函数实际上并没有使用任何被析构函数破坏的东西,所以对 foo 的调用“有效”,因为没有任何东西被使用被摧毁。 [这绝不是保证 - 它只是碰巧起作用,有点像有时不看就过马路是可以的,而其他时候则不然。根据道路的不同,这可能是一个非常糟糕的主意,或者在大多数情况下可能会奏效 - 但人们说“向左看,向右看,向左看,然后在安全的情况下穿过”(或类似的东西)是有原因的那个)]

在已销毁的对象上调用 delete 也是 UB,因此,它“有效”(在“不会导致程序崩溃”的意义上)再次纯属运气.

同时将 deletenew [] 混合使用或相反也是 UB - 同样,编译器 [及其相关的运行时] 可能会做正确或错误的事情,具体取决于根据情况和条件。

不要依赖程序中的未定义行为 [1]。它一定会回来咬你。 C 和 C++ 有很多 UB 情况,至少了解最常见的情况是很好的,例如“销毁后使用”、“释放后使用”等,并注意此类情况 - 并避免不惜一切代价!

关于c++ - 我怎样才能理解这些析构函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34258299/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com