Destructors
破坏者
析构函数是一种特殊的成员函数时调用的对象的生存期结束了。析构函数的目的是释放对象在其生命周期中可能获得的资源。
句法
~ class_name ( | (1) | |
---|---|---|
virtual ~ class_name ( | (2) | |
decl-specifier-seq(optional) ~ class_name () = default; | (3) | (since C++11) |
decl-specifier-seq(optional) ~ class_name () = delete; | (4) | (since C++11) |
attr(optional) decl-specifier-seq(optional) id-expression ( void(optional) ) except(optional) attr(optional) ; | (5) | |
1%29典型的析构函数声明
2%29虚拟析构函数在基类中通常是必需的
3%29强制编译器生成析构函数。
4%29禁用隐式析构函数
5%29析构函数声明的正式语法
decl-specifier-seq | - | friend, inline, virtual, or nothing (no return type) |
---|---|---|
id-expression | - | within a class definition, the symbol ~ followed by the class name. Within a class template, the symbol ~ followed by the name of the current instantiation of the template. At namespace scope or in a friend declaration within a different class, nested-name-specifier followed by the symbol ~ followed by the class name which is the same class as the one named by the nested-name-specifier. In any case, the name must be the actual name of the class or template, and not a typedef. The entire id-expression may be surrounded by parentheses which do not change its meaning. |
attr(C++11) | - | optional sequence of any number of attributes |
except | - | exception specification as in any function declaration (either dynamic exception specification(deprecated) or noexcept specification(C++11)) Except that if no exception specification is explicitly provided, the exception specification is considered to be one that would be used by the implicitly-declared destructor (see below). In most cases, this is noexcept(true). Thus a throwing destructor must be explicitly declared noexcept(false). (since C++11) |
Except that if no exception specification is explicitly provided, the exception specification is considered to be one that would be used by the implicitly-declared destructor (see below). In most cases, this is noexcept(true). Thus a throwing destructor must be explicitly declared noexcept(false). | (since C++11) |
解释
每当对象%27s时调用析构函数寿命包括。
- 程序终止,用于静态对象存储时间线程退出,用于具有线程本地存储持续时间的对象。%28自C++11%29
- 线程退出,用于具有线程本地存储持续时间的对象。
%28自C++11%29
- 范围的结束,对于具有自动存储时间的对象和通过绑定到引用而延长生命的临时对象。
- 删除-表达式,用于具有动态存储持续时间的对象。
- 全部结束表达,为无名的临时人员
- 堆栈展开,对于具有自动存储持续时间的对象,当异常转义其块时,请不予理会。
还可以直接调用析构函数,例如销毁使用安置-新分配器::销毁%28%29,以破坏通过分配程序构造的对象。请注意,直接为普通对象(例如局部变量)调用析构函数时,在作用域末尾再次调用析构函数时,会调用未定义的行为。
在泛型上下文中,析构函数调用语法可以与非类类型的对象一起使用;这称为伪析构函数调用:成员存取操作员...
隐式声明析构函数
如果没有为类类型%28struct
,,,class
,或union
%29,编译器将始终将析构函数声明为inline public
它的阶级成员。
与任何隐式声明的特殊成员函数一样,隐式声明的析构函数的异常规范是不抛出的,除非任何潜在构造的基或成员的析构函数是潜在投掷%28,因为C++17%29隐式定义将直接调用具有不同异常规范%28的函数,直到C++17%29。实际上,隐式析构函数是noexcept
除非类被其析构函数为noexcept(false)
...
隐式删除声明析构函数
类的隐式声明或默认析构函数。T
未定义为%28,直到C++11%29定义为删除
%28自C++11%29(如果下列任何一项为真):
T
具有无法解构的非静态数据成员%28已删除或不可访问的析构函数%29。
T
具有无法解构的直接或虚拟基类%28已删除或不可访问的析构函数%29
T is a union and has a variant member with non-trivial destructor. | (since C++11) |
---|
T
是一个联合,并且有一个具有非平凡析构函数的变体成员。%28自C++11%29
- 隐式声明的析构函数是虚拟%28,因为基类有一个虚拟析构函数%29,查找解分配函数%28。
operator delete
()
结果调用模糊、删除或不可访问的函数。
平凡析构函数
类的析构函数T
如果以下所有内容都为真,则是微不足道的:
- 析构函数不是用户提供的%28,它要么被隐式声明,要么在其第一个声明%29上显式地定义为默认。
- 析构函数不是虚拟%28,也就是说,基类析构函数不是虚拟%29。
- 所有直接基类都有平凡的析构函数。
- 所有类类型为%28或类类型为%29的数组的非静态数据成员都具有简单的析构函数。
平凡的析构函数是不执行任何操作的析构函数。具有普通析构函数的对象不需要DELETE-表达式,可以通过简单地释放它们的存储来释放它们。所有与C语言%28POD类型%29兼容的数据类型都是可以销毁的。
隐式析构函数
如果未删除隐式声明的析构函数,则隐式定义%28,也就是说,编译器在ODR-使用这个隐式定义的析构函数有一个空体。
破坏序列
对于用户定义的或隐式定义的析构函数,在执行析构函数的主体后,编译器按照声明的顺序调用该类所有非静态非变体成员的析构函数,然后调用所有直接非虚拟基类的析构函数。反向施工顺序%28依次调用其成员和基类的析构函数等%29,然后,如果该对象属于大多数派生类,则调用所有虚拟基的析构函数。
即使当析构函数被直接调用%28e.g时也是如此。obj.~Foo(%2
9,返回语句在~Foo()不立
即将控制返回给调用方:它首先调用所有的成员和基析构函数。
虚拟析构函数
通过指向基的指针删除对象会调用未定义的行为,除非基类中的析构函数是虚拟*
二次
class Base {
public:
virtual ~Base() {}
};
class Derived : public Base {};
Base* b = new Derived;
delete b; // safe
二次
一个共同的指导方针是,基类的析构函数必须是公共和虚拟或受保护和非虚拟的...
纯虚拟析构函数
可以声明一个析构函数。纯虚拟例如,在一个需要抽象的基类中,但是没有其他合适的函数可以声明为纯虚拟的。这种析构函数必须有一个定义,因为在销毁派生类时总是调用所有基类析构函数:
二次
class AbstractBase {
public:
virtual ~AbstractBase() = 0;
};
AbstractBase::~AbstractBase() {}
class Derived : public AbstractBase {};
// AbstractBase obj; // compiler error
Derived obj; // OK
二次
例外
与任何其他函数一样,析构函数可以通过抛出例外%28这通常要求显式声明noexcept(false)
%29%28,因为C++11%29,但是如果这个析构函数恰好是在堆栈展开,,,std::terminate
而是被称为。
尽管std::uncaught_exception
有时可用于检测正在进行的堆栈展开,通常认为允许任何析构函数通过抛出异常来终止的做法都是错误的。不过,有些库仍然使用此功能,例如沙基和Galera 3,它依赖于无名临时人员的析构函数在构造临时表达式的完整表达式末尾抛出异常的能力。
例
二次
#include <iostream>
struct A
{
int i;
A ( int i ) : i ( i ) {}
~A()
{
std::cout << "~a" << i << std::endl;
}
};
int main()
{
A a1(1
A* p;
{ // nested scope
A a2(2
p = new A(3
} // a2 out of scope
delete p; // calls the destructor of a3
}
二次
产出:
二次
~a2
~a3
~a1
二次
© cppreference.com
在CreativeCommonsAttribution下授权-ShareAlike未移植许可v3.0。