簡體   English   中英

C++ - 與非平凡類成員類型的聯合?

[英]C++ - union with nontrivial class member type?

我正在使用一個聯合,該聯合的成員是使用菱形繼承的類,但程序在分配給該成員時遇到分段錯誤。

我的懷疑是我需要添加一些復制構造函數,但是經過多次嘗試,正確的方法仍然回避我。

我在這里包含了一個最小的、可重現的示例。

struct Base
{
    Base() : a(0) {}
    Base(int x) : a(x) {}

    int a;
};

struct Derived1 : virtual public Base
{
    Derived1() {}
};

struct Derived2 : virtual public Base
{
    Derived2() {}
};

struct Final : public Derived1, public Derived2
{
    Final() {}
};

union Example
{
    Final value;
    int i;
};

int main()
{
    Example example{ Final() };
    example.i = -1;

    /* Segfault on the line below. 
     * If the above line

       example.i = -1;
     
     * is removed, the segfault is not encountered. */

    example.value = Final();
}

我感謝任何知道如何做到這一點的人。

由於Base類是virtual ,很可能有一些內部控制結構,當您分配時,它們會被破壞

example.i = -1;

當您重新創造value ,例如

new(&example.value) Final();
example.value = Final();

在分配之前,分段錯誤消失。 雖然,我不會推薦這個黑客。


正如評論中已經提到的,如果您有 C++17 可用,則std::variant會更合適。 該示例將變為

std::variant<Final, int> example{ Final() };
example = -1;
example = Final();

從 c++11 開始,允許具有定義自己的構造函數和/或復制控制成員的成員的聯合,但是當聯合具有內置類型的成員時,我們可以使用普通賦值來更改聯合持有的值,但不適用於具有非平凡類類型成員的聯合。 當我們在類類型的成員之間來回切換聯合的值時,我們必須構造或銷毀該成員。 例如見波紋管代碼

#include <iostream>
#include <new> // for placement new

class Type   // non built in type with constructors
{
private:
    int val;

public:
    Type() : val{0} {    }

    explicit Type(int v) : val{v}  {  }

    Type(const Type &obj) : val{obj.val} {  }

    int getval()  {   return val;  }
};

class unionexample   // I enclose union with class for readability
{
private:
    enum { INT,TYPE } OBJ;

    union
    {
        Type Tval;  // non build in type
        int ival;   // build in type
    };

public:
    unionexample() : ival{0}, OBJ{INT} // default with int
    { }

    unionexample(const unionexample &obj) : OBJ{obj.OBJ}
    {
        switch (obj.OBJ)
        {
        case INT:
            this->ival = obj.ival;
            break;
        case TYPE:
            new (&this->Tval) Type(obj.Tval);
            break;
        }
    }

    unionexample &operator=(int v)
    {
        if (OBJ == TYPE)
        {
            Tval.~Type(); // if it is TYPE destruct it
        }

        ival = v; // assign

        OBJ = INT;

        return *this;
    }

    unionexample &operator=(Type v)
    {
        if (OBJ == TYPE)
        {
            Tval = v;  // if it is alderdy Type just assign
        }

        new (&Tval) Type(v); // else construct

        OBJ = TYPE;

        return *this;
    }

    void print()
    {
        switch (OBJ)
        {
        case INT:
            std::cout << "ival = " << ival << std::endl;
            break;

        case TYPE:
            std::cout << "Tval = " << Tval.getval() << std::endl;
            break;
        }
    }

    ~unionexample()
    {
        if (OBJ == TYPE)  // if it is TYPE we must destruct it
            Tval.~Type();
    }
};

int main()
{
    unionexample ue;
    ue.print();
    ue = Type(1);
    ue.print();
}

輸出:

ival = 0
Tval = 1

跑到這里

在你的情況下

int main()
{
    Example example{ value : Final() };     // construct with Final

    example.value.~Final();                 // destruct Final

    example.i = -1;                         // assign int built in type
    
    new(&example.value)  Final();           // construct
    
    example.value.~Final();                 // destruct finally
}

如果你問這個問題而不是學習目的,你可以像其他答案一樣使用std::variant

謝謝。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM