我对无限制工会及其在实践中的应用有一些疑问。 我们假设我有以下代码:
struct MyStruct
{
MyStruct(const std::vector<int>& a) : array(a), type(ARRAY)
{}
MyStruct(bool b) : boolean(b), type(BOOL)
{}
MyStruct(const MyStruct& ms) : type(ms.type)
{
if (type == ARRAY)
new (&array) std::vector<int>(ms.array);
else
boolean = ms.boolean;
}
MyStruct& operator=(const MyStruct& ms)
{
if (&ms != this) {
if (type == ARRAY)
array.~vector<int>(); // EDIT(2)
if (ms.type == ARRAY)
new (&array) std::vector<int>(ms.array);
else
boolean = ms.boolean;
type = ms.type;
}
return *this;
}
~MyStruct()
{
if (type == ARRAY)
array.~vector<int>();
}
union {
std::vector<int> array;
bool boolean;
};
enum {ARRAY, BOOL} type;
};
修改
答案 0 :(得分:2)
array.clear();
更改为array.~vector<int>();
说明:operator=
正在对未被破坏的对象使用放置new
,这可以做任何事情,但实际上你可以预期它会泄漏先前阵列使用的动态内存( clear()
不释放内存/更改容量,它只是破坏元素并更改size
)。
从9.5 / 2开始:
如果union的任何非静态数据成员具有非平凡的默认值 构造函数(12.1),复制构造函数(12.8),移动构造函数(12.8),复制赋值运算符(12.8),移动 赋值运算符(12.8)或析构函数(12.4),联合的相应成员函数必须是 用户提供或将为联盟隐式删除(8.4.3)。
因此,vector
构造函数,析构函数等不会自行启动:必须在需要时显式调用它们。
在9.5 / 3中有一个例子:
考虑以下联合:
union U {
int i;
float f;
std::string s;
};
由于std :: string(21.3)声明了所有特殊成员函数的非平凡版本,因此U将具有 隐式删除的默认构造函数,复制/移动构造函数,复制/移动赋值运算符和析构函数。 要使用U,必须由用户提供部分或全部这些成员函数。
最后一点 - “要使用U,必须由用户提供部分或全部这些成员函数。” - 似乎假设U
需要协调自己模糊的价值语义行为,但在你的情况下,周围struct
正在这样做,所以你不需要定义任何这些union
成员函数。
2:每当数组值被布尔值替换时,我们必须调用数组析构函数。如果在operator=
中新的数组值正在放置 - new
而不是已分配,那么旧数组也必须调用其析构函数,但使用operator=
时,使用std::vector<int>::operator=
会更有效内存足以容纳所有被复制的元素。基本上,您必须匹配构造和破坏。更新:根据您的评论,示例代码有一个错误。
3:为什么需要一个新的位置而不是像'array = ms.array'这样的东西?
array = ms.array调用this
,它始终假定operator=
指针指向已经正确构造的对象。在该对象内部,您可以期望有一个指针,该指针将为NULL或引用一些内部短字符串缓冲区,或者引用堆。如果你的对象没有被破坏,那么{{1}}可能会在伪指针上调用内存释放函数。 Placement new表示“忽略此对象将占用的内存的当前内容,并从头开始构建具有有效成员的新对象。
答案 1 :(得分:1)
union不声明默认构造函数,复制构造函数,复制赋值运算符或析构函数。
如果std::string
声明了特殊成员函数的至少一个非平凡版本(在这种情况下),前面提到的那些都被隐式删除,你必须声明(并定义)它们(...如果他们被使用,那就是这种情况。)
就此而言,该代码不正确且不能成功编译(这几乎与标准9.5第3标准中的示例完全相同,除非它是{{ 1}},而不是 std::string
)。
(如果没有正确指出,不适用于anon union)
关于问题(2):为了安全地切换联合,这是必要的,是的。标准明确规定在9.5平方4 [注]中
如果你考虑一下,这也是有道理的。在任何时候,一个数据成员最多只能在std::vector
中激活,并且它们不会被神奇地默认构建/销毁,这意味着您需要正确构造/破坏事物。否则使用union
作为其他东西是没有意义的(甚至是定义的)(不是你无法那样做,但它是未定义的。)
该对象不是一个指针,你不知道它是否在堆上分配(即使它是在堆上分配的,那么在中另一个对象,所以它仍然不允许删除它)。如果你不能打电话给union
,你怎么摧毁一个物体?如果你不能删除它,你如何分配一个对象 - 可能几次 - 没有泄漏?这并没有留下很多选择。就此而言,[注意]非常有意义。