放置 - 新的STL容器并在之后安全地销毁它

时间:2017-04-27 22:38:27

标签: c++ c++11 destructor unions placement-new

此代码实现了一个不受限制的联合,它通过名称和索引为其三个成员中的任何一个提供访问。

由于std::string非常简单地构造和销毁,我需要为union提供特殊的构造函数和析构函数。

#include <iostream>
#include <string>

using namespace std ;

union MyUnion{
    string parts[3] ;
    struct{ string part1, part2, part3 ; } ;

    MyUnion(){
        new(parts+0) string ; //constructs the 3 strings in-place
        new(parts+1) string ;
        new(parts+2) string ;
    }
    ~MyUnion(){
        parts[0].~string() ; //calls string's destructor
        parts[1].~string() ;
        parts[2].~string() ;
    }
} ;

int main(){

    MyUnion u ;

    u.part1 = "one" ; //access by name
    u.part2 = "two" ;
    u.part3 = "three" ;

    cout << u.parts[0] << endl ; //access by index
    cout << u.parts[1] << endl ;
    cout << u.parts[2] << endl ;
}

这个例子编译并且工作正常(貌似),但我的问题是:

  • 这样做是否安全?
  • 我可以确定没有内存泄漏吗?
  • 如果string的构造函数抛出异常怎么办?是否需要捕获它以便不试图破坏从未构造的对象?

注意

代码在VC2015中编译,它支持未命名的结构。请忽略这一细节。

2 个答案:

答案 0 :(得分:2)

  

这样做是否安全?

没有。首先,常见的初始序列规则只允许读取成员,而不是写:

  

在具有结构类型T1的活动成员(9.3)的标准布局联合中,允许读取另一个的非静态数据成员m提供T2结构类型m的联合成员是T1T2的共同初始序列的一部分;行为就像提名T1的相应成员一样。

其次,常见的初始序列是标准布局类型的特征:

  

两个标准布局结构(第9节)类型的公共初始序列是[...]

std::string不需要是标准布局。

答案 1 :(得分:0)

  

这样做是否安全?

这取决于你愿意称之为安全的东西。代码肯定通过对标准的任何合理解释来调用未定义的行为。

除非涉及共同的子序列(9.3 Unions),否则您无法读取联合的非活动成员,但这些联合成员没有共同的初始序列,因为该概念仅针对两个标准布局结构定义( 9.2集体成员/ 20)和工会的一个成员根本不是结构。它是一个数组,所以它不能有任何共同的初始序列。

这也适用于使用基本类型的类似代码,例如int x[3];struct {int x0, x1, x2};。我们甚至不能保证x2x[2]具有相同的地址。