我对无限制工会及其在实践中的应用有一些疑问。假设我有以下代码:
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;
};
Run Code Online (Sandbox Code Playgroud)
编辑:
array.clear();改为array.~vector<int>();说明:operator=正在new对尚未被破坏的对象使用放置,这可以做任何事情,但实际上您可以预期它会泄漏前一个数组一直在使用的动态内存(clear()不会释放内存/更改容量,它只是破坏元素和变化size)。
从 9.5/2 开始:
如果联合体的任何非静态数据成员具有非平凡的默认构造函数 (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;
};
Run Code Online (Sandbox Code Playgroud)
由于 std::string (21.3) 声明了所有特殊成员函数的非平凡版本,因此 U 将具有隐式删除的默认构造函数、复制/移动构造函数、复制/移动赋值运算符和析构函数。要使用 U,这些成员函数中的部分或全部必须由用户提供。
最后一点 - “要使用 U,这些成员函数中的部分或全部必须由用户提供。” - 似乎假设U需要协调其自己模糊的值语义行为,但在您的情况下,周围struct正在这样做,因此您不需要定义任何这些union成员函数。
2:每当数组值被布尔值替换时,我们都必须调用数组析构函数。如果在operator=新数组中放置new而不是分配值,则还必须调用旧数组的析构函数,但当operator=现有内存足以容纳所有要复制的元素时,使用会更有效。基本上,您必须匹配构造和破坏。更新:根据您下面的评论,示例代码有一个错误。
3:为什么需要放置 new 而不是仅仅执行诸如 'array = ms.array' 之类的操作?
array = ms.array 调用std::vector<int>::operator=始终假定this指针指向已正确构造的对象。在该对象内部,您可以预期有一个指针,该指针要么为 NULL,要么引用某些内部短字符串缓冲区,或者引用堆。如果您的对象尚未被破坏,那么operator=很可能会在伪造的指针上调用内存释放函数。Placement new 表示“忽略该对象将占用的内存的当前内容,并从头开始构造一个具有有效成员的新对象。