30

C++11 允许在 a 中使用标准布局类型union联合成员具有用户定义的构造函数

union那么我的问题是:当超出范围时,我是否保证将调用自定义析构函数?

我的理解是切换时必须手动销毁和构造:http: //en.cppreference.com/w/cpp/language/union#Explanation

但是像这样的例子呢:

{
    union S { string str;
              vector<int> vec;
              ~S() {} } s = { "Hello, world"s };
}

s超出范围时,我是否因为没有调用string析构函数而泄漏了堆上分配的字符串的内存?

4

3 回答 3

26

在您提供的示例中,str不会被破坏。[class.union]/2 中的标准状态

联合可以具有成员函数(包括构造函数和析构函数),但不能具有虚拟 (10.3) 函数。联合不应有基类。联合不应用作基类。如果联合包含引用类型的非静态数据成员,则程序格式错误。最多一个联合的非静态数据成员可以有一个大括号或相等初始化器。[注意:如果联合的任何非静态数据成员具有非平凡的默认构造函数 (12.1)、复制构造函数 (12.8)、移动构造函数 (12.8)、复制赋值运算符 (12.8)、移动赋值运算符 (12.8),或析构函数(12.4),联合的相应成员函数必须是用户提供的,否则它将为联合隐式删除(8.4.3)。——尾注]

强调我的

因此,由于两者strvec都有特殊的成员函数,你需要自己为联合提供它们。

请注意,根据bogdan在空析构函数下方的评论是不够的。在 [class.union]/8 我们有

[...]如果 X 是一个联合,它的变体成员是非静态数据成员;[...]

所以这个联盟的所有成员都是变种。然后,如果我们查看 [class.dtor]/8 我们有

在执行析构函数的主体并销毁主体内分配的任何自动对象后,类 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::variantboost::variant

于 2016-10-18T11:40:43.780 回答
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;
            ^
于 2016-10-18T11:38:13.037 回答
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

于 2016-10-18T12:43:48.373 回答