我看到的无限制工会的例子似乎总是在构建时使用新的位置。维基百科有关C ++ 11特性的文章在union的构造函数中使用了new。
https://en.wikipedia.org/wiki/C%2B%2B11#Unrestricted_unions
#include <new> // Required for placement 'new'.
struct Point {
Point() {}
Point(int x, int y): x_(x), y_(y) {}
int x_, y_;
};
union U {
int z;
double w;
Point p; // Illegal in C++03; legal in C++11.
U() {new(&p) Point();} // Due to the Point member, a constructor definition is now required.
};
是否有必要在这里使用新的位置?例如,这段代码在没有警告的情况下使用gcc编译,而valgrind在使用union来保存字符串时没有显示内存泄漏:
struct HasUnresUnion
{
enum { Int, String } tag;
HasUnresUnion(int i)
: tag(Int),
as_int(i)
{}
HasUnresUnion(std::string str)
: tag(String),
as_str(std::move(str))
{}
~HasUnresUnion()
{
using std::string;
if (tag == String)
as_str.~string();
}
union
{
int as_int;
std::string as_str;
};
};
这似乎没有任何含糊之处,所以我不明白为什么标准会取缔这个。这是合法代码吗?如果联合未初始化(而不是分配给),是否需要新的展示位置?是否需要联盟中的构造函数?我确实看到没有自己的构造函数的无限制工会,但维基百科明确表示它是必需的。
答案 0 :(得分:9)
您展示的代码段非常安全;在初始化类似联合的类时,法律允许您初始化一个非静态数据成员。
维基百科文章中有一个示例,其中使用 placement-new ,因为他们在可以直接初始化某个成员的点之后写入成员。
union A {
A () { new (&s1) std::string ("hello world"); }
~A () { s1.~basic_string<char> (); }
int n1;
std::string s1;
};
然而,上一个代码段在语义上等同于以下内容,其中我们明确声明在构造A::s1
时应初始化A
。
union A {
A () : s1 ("hello world") { }
~A () { s1.~basic_string<char> (); }
int n1;
std::string s1;
};
在您的代码段中,您的班级中有一个匿名联盟(这使您的班级成为类似联盟的班级),这意味着除非您初始化一个1}}在成员初始化期间的成员 - 您必须使用 placement-new 在以后初始化它们。
union
在联合中,最多只有一个非静态数据成员可以在任何时候处于活动状态 时间,即至多一个非静态数据成员的值即可 随时存放在工会中。
9.5/1 -- Unions -- [class.union]p1
[...]
类型为3.8/1 -- Object lifetime -- [basic.life]p1
的对象的生命周期始于:
- 获得具有
T
类型的正确对齐和大小的存储,并且- 如果对象具有非平凡的初始化,则其初始化完成。
类型
T
的对象的生命周期在以下时间结束:
- 如果
T
是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者- 对象占用的存储空间被重用或释放。
答案 1 :(得分:1)
不,此处不需要展示新品。该标准规定,在不受限制的联合的情况下,应明确调用字段构造函数,否则字段将是未初始化的。
您可以使用传统方式调用构造函数
U(): p() {}
和异国情调的方式
U() { new(&p) Point(); }
如果在调用U
的构造函数之前无法构造字段,则第二个变体可能很有用。
在这种情况下,不要忘记放置析构函数。