Bla*_*ger -1 c++ destructor language-design explicit-destructor-call
析构函数的语法是~classname. 这导致需要在析构函数调用中显式写入对象的类型。为了避免这种情况,C++17 引入了std::destroy_at.
那么,Bjarne Stroustrup 选择~classname析构函数语法的最初理由是什么?如果语法不依赖于类型,std::destroy_at则不需要。
首先,我不确定~in的最初理由~T()。问题是,大多数时候如何手动调用析构函数并不重要。
通常你永远不会显式调用析构函数。大多数变量应该使用自动存储:
{
foo x;
}
Run Code Online (Sandbox Code Playgroud)
当超出范围x时,会自动调用 的析构函数。x显式调用 的析构函数x是错误的,因为一旦超出范围,就会再次调用它,从而导致未定义的行为。
有时您需要动态分配(通过智能指针进行分配,但就此处而言,手动是“可以”):
foo* p = new foo();
delete p;
Run Code Online (Sandbox Code Playgroud)
您必须delete通过 . 创建的内容new。同样,在这里显式调用析构函数也是错误的,因为delete调用析构函数并释放分配的内存。仅调用析构函数不会释放内存,并且调用析构函数两次是未定义的行为。
您必须显式调用析构函数的唯一情况是当您分配内存并通过放置新分两个单独的步骤创建对象时:“放置新”有什么用途?。
新的展示位置并不是您每天都使用的东西。它主要在图书馆中找到。在这种情况下,您需要显式调用析构函数,并且在这种情况下std::destroy_at可以很方便。
假设您编写以下代码:
void foo() {
auto x = returns_some_pointer();
// ....
... now delete *x ...
}
Run Code Online (Sandbox Code Playgroud)
假设returns_some_pointer返回一个指向某个对象的指针。进一步假设该对象是通过放置 new 创建的,并且在最后foo我们要调用它的析构函数。foo不需要知道x任何东西的类型。要调用析构函数,它需要知道T能够调用~T(). 类型可以从x自身推断出来,但这不太方便。一个非常方便的推导方式T就是使用函数模板,那就是destroy_at(x);.
TL;DR:对于大多数情况,如何调用析构函数并不重要,因为它要么被自动调用,要么通过调用delete. std::destroy_at不是析构函数的替代品,而是一种推断要调用其析构函数的对象类型的便捷方法。
构造函数和析构函数从很早开始就是 C++ 的一部分。(它们早于“C++”这个名称。)它们早于模板以及使vector模板成为可能的标准容器类型。在早期,很少或根本没有允许手动构建/销毁作为独立于 和 的操作new(delete例如,没有新的放置)。也不支持嵌套类。
总而言之,除了作为其定义的一部分之外,毫无疑问需要引用析构函数,并且毫无疑问在形成析构函数的标识符时会存在任何疑问或复杂性。
在类之后命名构造函数符合 C 声明的一般趋势,类似于它们所声明的事物的用法(例如,产生int *a(int b)格式*a(b)良好的结果)。析构函数在概念上与构造函数相关联,因此类名出现在构造函数中而不是析构函数中会很奇怪。由于除了 之外(当时)不支持运算符重载operator=,因此不存在~classname与重载的混淆的问题operator~。所以这是一个简单、低风险、略带诙谐的语法细节。