C++ 编译器如何知道,对于在同一地址构造两次的对象,必须调用第二个析构函数?
在下面的代码中,类C++ 编译器如何知道,对于在同一地址构造两次的对象,必须调用第二个析构函数?,c++,constructor,destructor,placement-new,C++,Constructor,Destructor,Placement New,在下面的代码中,类C中的对象sub构造了两次。第一个构造调用默认的ctorSub(),第二个构造使用placement new在相同的地址中重建此对象 因此析构函数也被称为两次。第一个调用使用对~C()中的Subdtor的直接调用,第二个调用在main()结束后调用,我相信是通过atexit()函数调用的 假设对象sub在同一地址重建,编译器如何知道必须在main()之后调用第二个析构函数?他把这些信息保存在哪里 #include <iostream> using namespace
C
中的对象sub
构造了两次。第一个构造调用默认的ctorSub()
,第二个构造使用placement new
在相同的地址中重建此对象
因此析构函数也被称为两次。第一个调用使用对~C()
中的Sub
dtor的直接调用,第二个调用在main()
结束后调用,我相信是通过atexit()
函数调用的
假设对象sub
在同一地址重建,编译器如何知道必须在main()
之后调用第二个析构函数?他把这些信息保存在哪里
#include <iostream>
using namespace std;
struct Table
{
int i;
Table(int j) : i(j) {}
};
struct Sub
{
Table* pTable;
Sub(int j) { cout << "ctor placement new" << endl; pTable = new Table(j); }
Sub() { cout << "ctor default" << endl; pTable = 0; }
~Sub() { if( pTable ) cout << "dtor placement new" << endl;
else cout << "dtor default" << endl;
delete pTable; pTable = 0; }
};
class C
{
Sub sub;
public:
C() { new (&sub) Sub(10); }
~C() { (&sub)->~Sub(); }
};
int main()
{
C c;
}
#包括
使用名称空间std;
结构表
{
int i;
表(int j):i(j){}
};
结构子
{
表*pTable;
Sub(int j){cout您关于atexit()
的假设不正确。当对象C
超出main()
的范围时,Sub
的析构函数被C
的析构函数调用
一个C++析构函数总是调用所有子对象的析构函数。
<>你的代码无论如何都是无效的,因为你在一个已经被构造成对象的内存块(<代码>子/代码>)上调用了放置new操作符。类似于析构函数,C++构造函数总是调用所有子对象的构造函数。 关于<代码> ATEXIT()的假设。
不正确。当对象C
超出main()
中的范围时,sub
的析构函数被C
的析构函数调用
一个C++析构函数总是调用所有子对象的析构函数。
您的代码仍然无效,因为您正在内存块(
sub
)上调用placement new运算符类似于析构函数,C++构造函数总是调用所有子对象的构造函数。 当C超出范围时,调用它的析构函数。C析构函数将显式调用子析构函数。当C析构函数完成时,子析构函数也将被调用(再次)。,因为所有C++析构函数都会自动调用所有内部对象的析构函数。
本质上,代码
(&sub)->~Sub();
不必要且不正确。您永远不应该显式调用托管对象的析构函数
编辑:
对通过placement new构造的对象显式调用析构函数是有效的。但是,只有当对象未被管理时,才会出现这种情况。例如:
class C
{
Sub sub[1];
public:
C() { new (sub) Sub(10); }
~C() { sub->~Sub(); }
};
这不仅是有效的,而且是必要的,因为C的成员属于Sub[1]类型(或者更一般地说是Sub*),因此当C被销毁时,不会显式调用Sub的析构函数。当C超出范围时,会调用其析构函数。C析构函数将显式调用Sub析构函数。当C析构函数完成时,也会(再次)调用Sub析构函数,因为所有C++析构函数都会自动调用所有内部对象的析构函数。 本质上,代码
(&sub)->~Sub();
不必要且不正确。您永远不应该显式调用托管对象的析构函数
编辑:
对通过placement new构造的对象显式调用析构函数是有效的。但是,只有当对象未被管理时,才会出现这种情况。例如:
class C
{
Sub sub[1];
public:
C() { new (sub) Sub(10); }
~C() { sub->~Sub(); }
};
这不仅是有效的,而且是必要的,因为C的成员是Sub[1]类型(或者更一般地说是Sub*),所以当C被销毁时,Sub的析构函数不会被显式调用。虽然这显然是未定义的行为,但如果你能解释发生了什么,这是非常明显的 创建C类的对象。在此过程中,Sub的默认构造函数被隐式调用。pTable为0。然后,显式调用int构造函数,初始化pTable。然后,在析构函数中,显式调用Sub的析构函数。pTable再次设置为0。然后,在C的析构函数末尾,Sub的析构函数被称为ag艾因,含蓄地
它不是在main的末尾发生的。它是在C的析构函数的末尾发生的。虽然这显然是未定义的行为,但如果你解释发生了什么,这是非常明显的 创建C类的对象。在此过程中,Sub的默认构造函数被隐式调用。pTable为0。然后,显式调用int构造函数,初始化pTable。然后,在析构函数中,显式调用Sub的析构函数。pTable再次设置为0。然后,在C的析构函数末尾,Sub的析构函数被称为ag艾因,含蓄地
它不是在main的末尾发生的。它发生在C的析构函数的末尾。其他答案对未定义的行为提出了有效的观点,但我很惊讶没有人提到这个程序应该如何更正
Sub
的析构函数确实需要在此处手动调用,但它需要在新放置之前调用,而不是在销毁期间调用:
class C
{
Sub sub;
public:
C() {
(&sub)->~Sub();
new (&sub) Sub(10);
}
};
在当前代码中,您创建了一个C
的实例,默认情况下,该实例构造了一个Sub
的实例,然后立即将另一个Sub
实例放置在旧实例的顶部,而不调用其析构函数。在mainC
的末尾,调用了Sub
的析构函数ex然后,在已经被破坏的对象上再次调用sub
的析构函数。这不仅是未定义的行为,如果分配了sub
的默认构造函数,还将导致内存泄漏
在更正后的代码中,sub是默认构造的,手动销毁,通过placement new构造,然后隐式销毁