在下面的 C++ 代码中,我可以显式调用析构函数,但不能显式调用构造函数。这是为什么?不是明确的 ctor 调用与 dtor 案例更具表现力和统一性吗?
class X { };
int main() {
X* x = (X*)::operator new(sizeof(X));
new (x) X; // option #1: OK
x->X(); // option #2: ERROR
x->~X();
::operator delete(x);
}
在下面的 C++ 代码中,我可以显式调用析构函数,但不能显式调用构造函数。这是为什么?不是明确的 ctor 调用与 dtor 案例更具表现力和统一性吗?
class X { };
int main() {
X* x = (X*)::operator new(sizeof(X));
new (x) X; // option #1: OK
x->X(); // option #2: ERROR
x->~X();
::operator delete(x);
}
X
因为在构造函数启动之前,该地址没有类型的对象。因此,取消引用x
作为一种X
类型或访问它的成员/方法将是未定义的行为。
x->X();
因此, (假设语法)和(假设语法)之间的主要区别在于x->~X()
,在第二种情况下,您有一个可以调用(特殊)成员(例如析构函数)的对象,而在第一种情况下,还没有可以调用的对象调用方法(甚至是特殊方法——构造函数)。
你可能会争辩说这条规则可能有一个例外,但最终这将是一个语法偏好问题,在这两种情况下你都有不一致的地方。使用当前语法,对构造函数的调用看起来不像对构造函数的调用,在您建议的语法中,与析构函数调用将是对称的,但是当您可以取消引用/访问对象的方法时,管理规则的不一致。实际上,必须有一个例外,允许在还不是对象的东西上调用方法。然后你必须在标准的字母中严格定义还不是对象的东西。
这是鸡和蛋问题的变体。
您可以显式调用析构函数,就好像它们是成员函数一样,因为对象的实例已经存在。
你不能对构造函数做同样的事情,因为你要调用它的实例需要存在,并且由构造函数完全初始化。
唯一的例外是您为对象分配了内存,但尚未初始化实例(即,实例的内存已存在,但尚未初始化为实际实例)。因此,您需要调用构造函数。new
这是您在“选项 1”注释下显示的语法放置有用的情况。但是,这不是您对实例执行的成员调用,因为在进行该调用之前该实例不可用。
您可以使用placement new 在任意位置构造对象。
new() 调用可以覆盖参数;放置构造函数采用void*
, 或指向类型的指针。new() 函数总是带一个 size_t 的参数,即 sizeof() 类型;这通常仅由全局新功能使用
写内存池时使用放置构造函数和显式析构函数。
例如(根据记忆!)
class MyClass
{
public:
inline new(size_t size, MyClass *ptr) { return ptr; };
};
它是这样使用的
{
MyClass *x = ...;
MyClass *y = new (x) MyClass(construct parameters);
x->~MyClass();
}
编辑以纠正@Ben-Voigt 指出的错误