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

Placement-new an STL container and destroying it safely afterwards

本文关键字:安全 之后 STL 放置      更新时间:2023-10-16

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

由于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中编译,VC2015 确实支持未命名的结构。请忽略这个细节。

这样做

安全吗?

不。首先,通用的初始序列规则只允许读取成员,不允许写入:

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

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

两种标准布局结构(条款 9)类型的常见初始序列是 [...]

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

这样做

安全吗?

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

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

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