不受限制的联合是不是需要放置 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 和构造函数定义?的主要内容,如果未能解决你的问题,请参考以下文章

C ++,是不是可以直接调用构造函数,而不需要new?

F#如何在递归可区分联合中指定类型限制

运算符new的执行顺序和构造函数的参数

没有默认构造函数可用于指定的类,结构或联合[重复]

JS构造函数加new与不加new的区别?

为啥我的 Solidity 构造函数需要两个未在参数中指定的变量?