不受限制的联合是不是需要放置 new 和构造函数定义?
Posted
技术标签:
【中文标题】不受限制的联合是不是需要放置 new 和构造函数定义?【英文标题】:Do unrestricted unions require placement new and a constructor definition?不受限制的联合是否需要放置 new 和构造函数定义? 【发布时间】:2015-10-10 20:36:20 【问题描述】:我见过的无限制联合的例子似乎总是在构造时使用placement new。 C++11 特性的 Wikipedia 文章在联合的构造函数中使用放置 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.
;
这里有必要使用placement new吗?例如,这段代码使用 gcc 编译时没有警告,当使用 union 保存字符串时,valgrind 没有显示内存泄漏:
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;
;
;
这里似乎没有任何歧义,所以我不明白为什么该标准会禁止这样做。这是合法的代码吗?当联合未初始化(而不是被分配到)时是否需要放置新的?是否需要联合中的构造函数?我肯定见过没有自己的构造函数的无限制联合,但***明确指出这是必需的。
【问题讨论】:
只是有点相关的说明:也可以使用构造函数初始化列表。 @JoachimPileborg OP 不就是这样做的吗? (或者,至少,询问是否允许。) @JoachimPileborg 我不明白这个评论。 :( 代码使用成员初始化列表。 你 structure 使用它,但你也可以将它用于联合构造函数。无需放置新的。我不知道规范是否真的允许(现在不要“在我身上”),但我不明白为什么不这样做。构造器是构造器是构造器。 @JoachimPileborg 啊,明白了。我只是对赋予我的工会成员职能感到奇怪,但即使工会有一名演员,问题也是一样的。 【参考方案1】:简介
你展示的sn-p是绝对安全的;在初始化 union-like 类时,您可以合法地初始化 一个 非静态数据成员。
示例
***文章有一个示例,其中使用了 placement-new,因为它们在可以直接初始化某个成员的点之后写入成员。
union A
A () new (&s1) std::string ("hello world");
~A () s1.~basic_string<char> ();
int n1;
std::string s1;
;
然而,前面的 sn-p 在语义上等价于下面的,我们明确指出在构造 A
时应初始化 A::s1
。
union A
A () : s1 ("hello world")
~A () s1.~basic_string<char> ();
int n1;
std::string s1;
;
细化
在您的 sn-p 中,您的类中有一个 匿名联合(这使您的类成为 类似联合的类),这意味着除非您初始化 union
的成员之一 在类的初始化期间 - 您必须在以后使用 placement-new 来初始化它们。
标准是怎么说的?
9.5/1 -- <b>Unions</b> -- [class.union]p1
在一个联合中,最多有一个非静态数据成员可以在任何时候处于活动状态 时间,即最多可以有一个非静态数据成员的值 随时存储在联合中。
3.8/1 -- <b>Object lifetime</b> -- [basic.life]p1
[...]
获得了与T
类型对象的生命周期开始于:T
类型正确对齐和大小的存储,并且 如果对象有非平凡的初始化,它的初始化就完成了。如果
T
类型对象的生命周期在以下时间结束:T
是具有非平凡析构函数 (12.4) 的类类型,则析构函数调用开始,或者 对象占用的存储空间被重用或释放。
【讨论】:
【参考方案2】:不,此处不需要放置新位置。标准规定,在不受限制的联合的情况下,应显式调用字段构造函数,否则字段将未初始化。
您可以使用传统方式调用构造函数
U(): p()
异国风情
U() new(&p) Point();
如果在调用U
的构造函数之前无法构造字段,则第二种变体可能有用。
在这种情况下也不要忘记放置析构函数。
【讨论】:
联合本身是否需要构造函数?像我举的例子一样合法吗? 是的,如果任何联合字段包含非平凡的特殊成员函数,则需要显式定义的构造函数。是的,代码似乎合法。但是如果你在实际项目中使用这段代码,你可能会被人手。 展示位置删除?通常与placement new 类似的是手动调用析构函数。 是的,我的意思是析构函数以上是关于不受限制的联合是不是需要放置 new 和构造函数定义?的主要内容,如果未能解决你的问题,请参考以下文章