在实践中不受限制的结合

时间:2013-05-21 05:46:00

标签: c++ c++11 unions

我对无限制工会及其在实践中的应用有一些疑问。 我们假设我有以下代码:

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;
};
  1. 这段代码有效吗??
  2. 每次我们使用布尔值时都需要显式调用向量析构函数(如此处所述http://cpp11standard.blogspot.com/2012/11/c11-standard-explained-1-unrestricted.html
  3. 为什么需要新的展示位置,而不仅仅是做&#39; array = ms.array&#39; ?
  4. 修改

    • 是的,它编译
    • &#34;在匿名联合内部声明的成员实际上是包含类的成员,并且可以在包含类的构造函数中初始化。&#34; (C++11 anonymous union with non-trivial members
    • 按照建议添加显式析构函数,使用g ++ 4.8 / clang 4.2
    • 导致SIGSEV

2 个答案:

答案 0 :(得分:2)

  1. 代码的错误:将array.clear();更改为array.~vector<int>();
  2. 说明: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,你怎么摧毁一个物体?如果你不能删除它,你如何分配一个对象 - 可能几次 - 没有泄漏?这并没有留下很多选择。就此而言,[注意]非常有意义。

相关问题