我对以下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

这是我不明白的:
  • 为什么在调用析构函数后可以调用foo
  • 为什么在调用析构函数后可以调用delete
  • 如果我注释掉delete b;,此代码会泄漏内存吗?
  • A的析构函数是虚拟的。我认为不会调用子类中重载的虚函数。为什么~A()会被调用?
  • 如果我注释掉b->~B();,则在B dtor之后打印foo行。为什么?
  • 如果我重复b->~B();行两次,则输出为:B dtor\nA dtor\nA dtor。 ??
  • 如果我用delete B;切换delete[] b;,我将得到相同的输出。我认为第二个是正确的,因为b是使用new[]创建的,但这没关系,因为我只是将B的一个实例推入堆中。那是对的吗?

  • 很抱歉问了这么多问题,但这令我感到困惑。如果我的个人问题被误导了,请告诉我了解每个析构函数何时运行所需的知识。

    最佳答案

    “未定义的行为”(简称UB)是允许编译器执行任何操作的位置-这通常意味着介于“崩溃”,“给出错误的结果”和“仍然执行您期望的操作”之间。您的b->foo()绝对是未定义的,因为它发生在b->~B()调用之后,

    由于您的foo函数实际上并没有使用析构函数销毁的任何东西,因此对foo的调用“有效”,因为没有使用任何已销毁的东西。 [这是绝对不能保证的-它只是工作而已,有点像有时候跨过马路不看就好,有时则不然。视其路况而定,这可能不是一个好主意,或者在大多数情况下可能会奏效-但是有一个原因,人们说“向左看,向右看,向左看,然后在安全的情况下越过”(或类似的意思)那)]

    在已被破坏的对象上调用delete也是UB,所以再次幸运的是,它“有效”(在“不会导致程序崩溃”的意义上)。

    UB-将deletenew []混合使用,或者反之亦然-UB –再次,编译器[及其相关的运行时]可能会根据情况和条件做对或错事。

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

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

    10-11 20:31
    查看更多