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
的析构函数而泄漏了在堆上分配的字符串的内存?
在您的示例中,您提供的str
将不会被销毁。[class.union]/2
联合可以有成员函数(包括构造函数和析构函数),但不能有虚函数(10.3)。联合不能有基类。联合不能用作基类。如果联合包含引用类型的非静态数据成员,则程序是病态的。联合中最多只能有一个非静态数据成员具有大括号或相等初始化式。[>注意:如果联合的任何非静态数据成员具有重要的默认构造函数(12.1)、复制构造函数(12.8)、移动构造函数(12.8)、复制赋值操作符(12.8)、移动赋值操作符(12.8)或析构函数(12.4),则联合的相应成员函数必须由用户提供,否则将为联合隐式删除(8.4.3)。- ]
<>共舞,强调我的
由于str
和vec
都有特殊的成员函数,所以你需要自己为联合提供它们。
请注意,根据bogdan下面的注释,空析构函数是不够的。在类。Union]/8我们有
[…如果X是一个联合,它的变体成员是非静态数据成员;[…]
所以这个并集的所有成员都是变体。然后如果我们看[类。我们有
在执行析构函数体并销毁在析构函数体中分配的任何自动对象之后,类X的析构函数调用X的直接非变量非静态数据成员的析构函数[…]
因此析构函数不会自动销毁联合的成员,因为它们是变体。
你可以创建一个带标签的联合,就像kennytm在这里做的那样
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
您的示例无法编译。默认情况下,联合具有一个已删除的析构函数。当然,应该调用什么析构函数?当然你不能两个都打。并且没有存储任何关于实际构造了哪个成员的信息。提供合适的析构函数取决于您。
下面是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;
^
总是需要用非平凡类型手动调用结构中对象的构造函数。
通常也需要显式地构造它们。这里的赋值工作起来似乎很奇怪。
如果有疑问,您可以在调用析构函数时检查程序集。
这段代码的程序集调用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