是联盟成员的析构函数

时间:2016-10-18 11:24:37

标签: c++ c++11 destructor unions standard-layout

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的析构函数?

3 个答案:

答案 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)。 - 结束说明]

强调我的

因为strvec都有特殊的成员函数,所以你需要自己为它们提供它们。

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

  

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

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

  

在执行析构函数的主体并销毁在主体内分配的任何自动对象之后,类X的析构函数调用X的直接非变量非静态数据成员的析构函数[...]

因此,析构函数不会自动销毁union的成员,因为它们是变体。

你可以tagged union kennytmhere

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

答案 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