C++ 是工会会员';s的析构函数调用
C++11允许在C++ 是工会会员';s的析构函数调用,c++,c++11,destructor,unions,standard-layout,C++,C++11,Destructor,Unions,Standard Layout,C++11允许在联合中使用标准布局类型: 我的问题是:当联合超出范围时,我是否保证调用自定义析构函数 我的理解是,我们必须在切换时手动销毁和构建: 但是像这样的例子呢: { union S { string str; vector<int> vec; ~S() {} } s = { "Hello, world"s }; } { 联合序列{string str; 向量向量机; ~S(){}S={“你好,世界”S}; }
联合中使用标准布局类型
:
我的问题是:当联合
超出范围时,我是否保证调用自定义析构函数
我的理解是,我们必须在切换时手动销毁和构建:
但是像这样的例子呢:
{
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
{
联合序列{string str;
向量向量机;
~S(){}S={“你好,世界”S};
}
当
s
超出范围时,我是否因为没有调用string
的析构函数而泄漏了堆上分配的字符串的内存?您的示例将无法编译。默认情况下,联合有一个已删除的析构函数。当然,应该叫什么析构函数?当然你不能两者都打电话。没有任何地方存储关于实际构造哪个成员的信息。这取决于你提供一个合适的析构函数
以下是试图编译代码段时GCC的输出:
In function ‘int main()’:
error: use of deleted function ‘main()::<anonymous union>::~<constructor>()’
vector<int> vec; } s = { "Hello, world"s };
^
note: ‘main()::<anonymous union>::~<constructor>()’ is implicitly deleted because the default definition would be ill-formed:
union { string str;
^
函数“int main()”中的:
错误:使用已删除的函数“main():~()”
向量向量向量;}s={“你好,世界”s};
^
注意:“main()::~()”被隐式删除,因为默认定义的格式不正确:
联合{string str;
^
在您的示例中,您提供的str
不会被破坏。标准状态在[class.union]/2中
联合可以有成员函数(包括构造函数和析构函数),但不能有虚函数(10.3)函数。联合不应具有基类。联合不得用作基类。如果联合包含引用类型的非静态数据成员,则程序格式错误。联合的最多一个非静态数据成员可以具有大括号或相等的初始值设定项。[注意:如果联合体的任何非静态数据成员具有非平凡的默认构造函数(12.1)、复制构造函数(12.8)、移动构造函数(12.8)、复制赋值运算符(12.8)、移动赋值运算符(12.8)或析构函数(12.4),则必须由用户提供联合体的相应成员函数,否则将被隐式删除(8.4.3)为工会。-结束说明
重点矿山
因此,由于str
和vec
都有特殊的成员函数,这些函数并不微不足道,因此您需要自己为工会提供它们
请注意,根据下面的注释,空的析构函数是不够的
[…]如果X是一个联合,其变量成员是非静态数据成员;[…]
所以这个联合体的所有成员都是变体。那么,如果我们看[class.dtor]/8,我们有
在执行析构函数体并销毁该体中分配的任何自动对象之后,类X的析构函数调用X的直接非变量非静态数据成员的析构函数[…]
因此,析构函数不会自动销毁联合体的成员,因为它们是变体
你可以做一个类似的决定
这可以确保正确的成员被销毁,或者只使用
std::variant
或boost::variant
您始终需要使用非平凡类型手动调用结构中对象的构造函数
通常你也需要显式地构造它们。这里的赋值似乎很奇怪
如果有疑问,如果调用析构函数,您可以随时检查程序集
此代码的程序集确实调用了basic_string
构造函数,但没有调用析构函数。因此,此处存在漏洞
using namespace std;
int main(int argc, char** argv){
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
使用名称空间std;
int main(int argc,字符**argv){
联合序列{string str;
向量向量机;
~S(){}S={“你好,世界”S};
}
链接以查看程序集:这甚至没有编译。好吧,你需要对这些东西使用placement new并手动调用构造函数。因此,为了保持一致,析构函数也是如此。@Hayt因此,在我编辑之后,这个构造函数将编译:但根据它确实会泄漏。我不确定为什么分配首先会起作用。它是eems是无意的。但它仍然泄漏。您可以在这里看到调用的是
basic_string
构造函数,而不是析构函数:是的,这是您应该做和必须做的。它与{}一起工作的情况
赋值似乎是联合体的一个设计缺陷,因为它隐式分配而非解除分配。我也同意nathan的说法。但如果有帮助的话,我可以添加一个带有一些解释的答案。谢谢你的评论。我已经清理了有问题的代码,向联合体
定义中添加了自定义析构函数。因此,感谢t他提供了一些信息,但这个答案并没有试图解决这个问题:当联合
超出范围时会发生什么?@JonathanMee我可以确保你的复制构造函数被删除。该标准甚至在下一段中有一个联合的示例,其中包含一个std::sting
,并且它声明了std::string(21.3)声明所有特殊成员函数的非平凡版本,U将有一个隐式删除的默认构造函数、复制/移动构造函数、复制/移动赋值运算符和析构函数。若要使用U,这些成员函数中的部分或全部必须是用户-provided@JonathanMee在这些例子中,没有一个编译器是有罪的n不涉及联合的复制构造函数;该联合是一个聚合,这就是聚合初始化(它涉及第一个联合成员的复制初始化,字符串
,它调用字符串
的复制构造函数)@JonathanMee确实删除了这些构造函数,但这并不一定意味着不可能进行任何构造。聚合仍然可以通过聚合初始化进行初始化,即使其所有构造函数都被删除。这不仅限于联合。联合特有的是联合聚合
using namespace std;
int main(int argc, char** argv){
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}