为什么如果它被删除则调用析构函数,如果没有删除则不调用它?

29 c++ destructor c++11

请考虑以下代码:

#include <iostream>

struct A 
{
    A(){ };
    ~A(){ std::cout << "~A::A()" << std::endl; };
};

struct B: A { };

B *b = new B; //Doesn't produce any side-effect.

int main(){ }
Run Code Online (Sandbox Code Playgroud)

DEMO

程序不会产生任何输出,这意味着不会调用析构函数.但是如果我们用说明delete符替换析构函数的主体,程序甚至不会编译.

#include <iostream>

struct A 
{
    A(){ };
    ~A() = delete; //{ std::cout << "~A::A()" << std::endl; };
};

struct B: A { };

B *b = new B; //Error: use of deleted function

int main(){ }
Run Code Online (Sandbox Code Playgroud)

DEMO

由于调用了删除的功能.那是在这种情况下被调用的析构函数.为什么会有这样的差异?

即使我们明确定义了B构造函数,它也不会起作用:

#include <iostream>

struct A 
{
    A(){ };
    ~A() = delete; //{ std::cout << "~A::A()" << std::endl; };
};

struct B: A 
{ 
    B(){ };
};

B *b = new B;

int main(){ }
Run Code Online (Sandbox Code Playgroud)

DEMO

vso*_*tco 16

问题是B编译器删除了构造函数,否则默认定义将是错误的.这是因为A没有析构函数,以及默认的构造函数B无法构造一个BA如果A不能被破坏.如果用g ++编译,那就是你得到的错误:

note: 'B::B()' is implicitly deleted because the default definition would be ill-formed:

或者铿锵++:

error: call to implicitly-deleted default constructor of 'B'

如果你B(){}明确地声明构造函数,那么它会因为删除而导致因为它无法破坏A部分而引发抱怨.在编译时检查隐藏其父级的能力,因此您收到错误.BA::~A()BA

这个问题的+1.看来你不能从带有删除的析构函数的类继承(然后使用实例),尽管这是我第一次碰到这个问题.


T.C*_*.C. 15

该标准的适用部分是(引用N4140):

§12.4[class.dtor]/p11:

如果调用析构函数或在5.3.4和12.6.2中指定,则可能会调用析构函数.如果可能调用的析构函数被删除或无法从调用的上下文访问,则程序格式不正确.

§12.6.2[class.base.init]/p10:

在非委托构造函数中,可能会调用每个可能构造的类类型子对象的析构函数(12.4).[ 注意:此规定确保在抛出异常时可以为完全构造的子对象调用析构函数(15.2).- 结束说明 ]

§12[特殊]/p5:

对于一个类,它的非静态数据成员,它的非虚拟直接基类,如果该类不是抽象的(10.4),它的虚拟基类被称为它可能构造的子对象.

由于A是一个非虚拟直接基B和因此潜在的子对象构成B,它的析构潜在调用B::B(),并自该析构函数被删除,是形成不良的节目.

另见CWG第1424期.


Jam*_*nze 11

至于是什么,我认为是你的基本的问题:你为什么不能建立一个B,尽管它只是析构函数A不存在:在构造函数B,编译器会自动生成代码调用析构函数A,如果有一个例外.如果A要用作基类,它必须具有可访问的析构函数(公共或受保护的).

当然,在你的情况下,构造函数B不能抛出,因此 A::~A()永远不会被调用.但编译器无法始终确定是否是这种情况,并且标准不要求它甚至尝试.编译器必须假设B::B()可能抛出的体(在完全构造之后A).即使它可以确定B::B()不能抛出,并且不生成调用析构函数的代码A,这也是一种优化,不允许改变代码的合法性.


小智 5

基于第一个代码:

#include <iostream>
struct A
{
  A(){ };
  ~A(){ std::cout << "~A::A()" << std::endl; };
};

struct B: A { };

B *b = new B; //Doesn't produce any side-effect.

int main(){ }
Run Code Online (Sandbox Code Playgroud)
  1. 对象b被声明为全局,因此它的生命周期与程序运行一样长.
  2. 对象b是动态分配的,因此需要"裸"删除.

请尝试以下方法:

#include <iostream>

struct A 
{
    A(){ };
    ~A(){ std::cout << "~A::A()" << std::endl; };
};

struct B: A { };

int main(){
    B b;
}
Run Code Online (Sandbox Code Playgroud)