为什么我允许声明一个对象的删除析构函数? [英] Why am I permitted to declare an object with a deleted destructor?

查看:141
本文介绍了为什么我允许声明一个对象的删除析构函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下文字:


[C ++ 11:12.4 / 11]:在程序终止时具有静态存储持续时间(3.7.1)的构造对象隐式调用析构函数





  • 用于具有自动存储持续时间的构造对象的线程存储持续时间(3.7.2)的构造对象

  • )当临时对象的生命周期结束(12.2)时,

  • 时,创建对象的块退出(6.7) >(5.3.5),
  • (5.3.5)对由 new-expression b $ b 也可以显式地调用析构函数。




    那么为什么这个程序会成功编译?

      #include< iostream> 

    struct A
    {
    A(){};
    〜A()= delete;
    };

    A * a = new A;

    int main(){}

    // g ++ -std = c ++ 11 -O2 -Wall -pedantic -pthread main.cpp& ./a.out

    GCC是否允许?






    我倾向于这样说,因为它拒绝了以下内容:该标准似乎没有特定规则专用于继承层次结构中删除的析构函数(唯一松散相关的措词与默认默认构造函数的生成有关):

      #include< iostream> 

    struct A
    {
    A(){};
    〜A()= delete;
    };

    struct B:A {};

    B * b = new B; //错误:使用删除的函数

    int main(){}


    解决方案

    第一部分没有错误,因为标准文本不适用 - 没有类型A的对象被声明。



    对于第二部分,让我们回顾一下对象构造的工作原理。标准表示(15.2 / 2),如果施工的任何部分抛出,所有完全构造的子对象直到那一点都以相反的施工顺序销毁。



    一个构造函数的代码如果用手写出来,看起来像这样:

      //给定:
    struct C:A,B {
    D d;
    C():A(),B(),d(){/ * more code * /}
    };

    //这是扩展的构造函数:
    C(){
    A();
    try {
    B();
    try {
    d.D();
    try {
    / * more code * /
    } catch(...){d。〜D();扔; }
    } catch(...){〜B();扔; }
    } catch(...){〜A();扔; }
    }

    对于更简单的类,默认构造函数是 new 表达式所需的)如下:

      B :: B(){
    A();
    try {
    //这里没有做什么
    } catch(...){
    〜A(); //错误:〜A()被删除。
    throw;
    }
    }

    抛出后初始化一些子对象已经完成就太复杂了,无法指定。因此,这实际上不会发生,因为B的默认构造函数隐式定义为删除,由于N3797 12.1 / 4中的最后一个点:



    < blockquote>

    类X的默认默认构造函数定义为删除如果:





    • 任何直接或虚拟基类或非静态数据成员都具有从默认默认构造函数中删除或无法访问的析构函数。


复制/移动构造函数在12.8 / 11中是第四个项目符号。



在12.6.2 / 10中还有一个重要的段落:


在非委托构造函数中,或虚拟基类,并且类类型的每个非静态数据成员都可能被调用。



Consider the following text:

[C++11: 12.4/11]: Destructors are invoked implicitly

  • for constructed objects with static storage duration (3.7.1) at program termination (3.6.3),
  • for constructed objects with thread storage duration (3.7.2) at thread exit,
  • for constructed objects with automatic storage duration (3.7.3) when the block in which an object is created exits (6.7),
  • for constructed temporary objects when the lifetime of a temporary object ends (12.2),
  • for constructed objects allocated by a new-expression (5.3.4), through use of a delete-expression (5.3.5),
  • in several situations due to the handling of exceptions (15.3).

A program is ill-formed if an object of class type or array thereof is declared and the destructor for the class is not accessible at the point of the declaration. Destructors can also be invoked explicitly.

Then why does this program compile successfully?

#include <iostream>

struct A 
{
    A(){ };
    ~A() = delete;
};

A* a = new A;

int main() {}

// g++ -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

Is GCC just being permissive?


I'm inclined to say so, since it rejects the following yet the standard appears to have no particular rule specific to deleted destructors in an inheritance hierarchy (the only loosely relevant wording is pertinent to the generation of defaulted default constructors):

#include <iostream>

struct A 
{
    A() {};
    ~A() = delete;
};

struct B : A {};

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

int main() {}

解决方案

The first part is not ill-formed because the standard text doesn't apply - no object of type A is declared there.

For the second part, let's review how object construction works. The standard says (15.2/2) that if any part of construction throws, all fully constructed subobjects up to that point are destroyed in reverse order of construction.

This means that the code underlying a constructor, if all written out by hand, would look something like this:

// Given:
struct C : A, B {
   D d;
   C() : A(), B(), d() { /* more code */ }
};

// This is the expanded constructor:
C() {
  A();
  try {
    B();
    try {
      d.D();
      try {
        /* more code */
      } catch(...) { d.~D(); throw; }
    } catch(...) { ~B(); throw; }
  } catch(...) { ~A(); throw; }
}

For your simpler class, the expanded code for the default constructor (whose definition is required by the new expression) would look like this:

B::B() {
  A();
  try {
    // nothing to do here
  } catch(...) {
    ~A(); // error: ~A() is deleted.
    throw;
  }
}

Making this work for cases where no exception can possibly be thrown after initialization for some subobject has been completed is just too complex to specify. Therefore, this doesn't actually happen, because the default constructor for B is implicitly defined as deleted in the first place, due to the last bullet point in N3797 12.1/4:

A defaulted default constructor for class X is defined as deleted if:

  • [...]
  • any direct or virtual base class or non-static data member has a type with a destructor that is deleted or inaccessible from the defaulted default constructor.

The equivalent language exists for copy/move constructors as the fourth bullet in 12.8/11.

There is also an important paragraph in 12.6.2/10:

In a non-delegating constructor, the destructor for each direct or virtual base class and for each non-static data member of class type is potentially invoked.

这篇关于为什么我允许声明一个对象的删除析构函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆