C ++ 11允许在union
中使用标准布局类型:Member of Union has User-Defined Constructor
我的问题是:当union
超出范围时,我是否保证会调用自定义析构函数?
我的理解是,我们必须在切换时手动销毁和构建:http://en.cppreference.com/w/cpp/language/union#Explanation
但是这样的例子怎么样:
{
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
当s
超出范围时,我是否将内存泄漏到堆上分配的字符串,因为我没有调用string
的析构函数?
答案 0 :(得分:17)
在您的示例中,您提供的str
不会被破坏。 [class.union] / 2
union可以具有成员函数(包括构造函数和析构函数),但不具有虚函数(10.3)。工会不得有基类。联合不得用作基类。如果联合包含引用类型的非静态数据成员,则程序格式错误。联合的至多一个非静态数据成员可以具有支撑或等于初始化器。 [注意:如果union的任何非静态数据成员具有非平凡的默认构造函数(12.1),复制构造函数(12.8),移动构造函数(12.8),复制赋值运算符(12.8),移动赋值运算符( 12.8)或析构函数(12.4),联合的相应成员函数必须由用户提供,否则将为联合隐式删除(8.4.3)。 - 结束说明]
强调我的
因为str
和vec
都有特殊的成员函数,所以你需要自己为它们提供它们。
请注意,根据下面的bogdan's条评论,空的析构函数是不够的。在[class.union] / 8中我们有
[...]如果X是一个联合,其变体成员是非静态数据成员; [...]
所以这个联盟的所有成员都是变种。然后,如果我们看[class.dtor] / 8,我们有
在执行析构函数的主体并销毁在主体内分配的任何自动对象之后,类X的析构函数调用X的直接非变量非静态数据成员的析构函数[...]
因此,析构函数不会自动销毁union的成员,因为它们是变体。
struct TU { int type; union { int i; float f; std::string s; } u; TU(const TU& tu) : type(tu.type) { switch (tu.type) { case TU_STRING: new(&u.s)(tu.u.s); break; case TU_INT: u.i = tu.u.i; break; case TU_FLOAT: u.f = tu.u.f; break; } } ~TU() { if (tu.type == TU_STRING) u.s.~string(); } ... };
确保正确的成员被销毁或仅使用std::variant
或boost::variant
答案 1 :(得分:4)
您的示例无法编译。默认情况下,联合会有一个已删除的析构函数。当然,应该叫什么析构函数?当然你不能同时打电话。并且没有任何关于哪个成员实际构建的信息。由你来提供适当的析构函数。
这是尝试编译代码片段时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;
^
答案 2 :(得分:1)
您总是需要使用非平凡类型手动调用结构中对象的构造函数。
通常你也总是需要明确地构建它们。这里的任务有效,这似乎很奇怪。
如果有疑问,你可以随时检查装配体是否被调用。
此代码的程序集确实调用basic_string
构造函数,但不调用析构函数。所以你会在这里泄漏。
using namespace std;
int main(int argc, char** argv){
union S { string str;
vector<int> vec;
~S() {} } s = { "Hello, world"s };
}
链接以查看程序集:https://godbolt.org/g/wKu3vf