2016-01-03 6 views
7

Bisher habe ich nur Gewerkschaften zum Speichern entweder Mitglied A oder Mitglieds B.
ich mich jetzt in dem Fall, wo ich Änderung während der Laufzeit das verwendete Mitglied werden soll.
C++ Union Nutzung

union NextGen { 
    std::shared_ptr<TreeRecord> Child = nullptr; 
    std::vector<std::shared_ptr<TreeRecord>> Children; 
}; 

Meine heutige Nutzung:

void TreeRecord::AddChild(const std::shared_ptr<TreeRecord>& NewChild) { 
    if(_childCount == 0) { 
     _nextGeneration.Child = NewChild; 
     _childCount++; 
    } else if(_childCount == 1) { 
     //This is not clear to me: 
     //Do I have to set Child to nullptr first? 
     //Do I need to clear the Children vecor? 
     //Or will it work like this? 
     _nextGeneration.Children.push_back(_nextGeneration.Child); 
     _nextGeneration.Children.push_back(NewChild); 
     _childCount++; 
    } else { 
     _nextGeneration.Children.push_back(NewChild); 
     _childCount++; 
    } 
} 

Neue Implementierung (versuchen):

typedef std::shared_ptr<TreeRecord> singlechild_type; 
typedef std::vector<std::shared_ptr<TreeRecord>> children_type; 



union { 
    singlechild_type _child; 
    children_type _children; 
}; 



void TreeRecord::AddChild(const singlechild_type& NewChild) { 
    if(_childCount == 0) { 
     _child = NewChild; 
     _childCount = 1; 

    } else if(_childCount == 1) { 
     singlechild_type currentChild = _child; //Copy pointer 
     _child.~singlechild_type(); //Destruct old union member 
     new (&_children) children_type(); //Construct new union member 
     _children.push_back(currentChild); //Add old child to vector 
     _children.push_back(NewChild); //Add new child to vector 
     _childCount = 2; 

    } else { 
     _children.push_back(NewChild); 
     _childCount++; 
    } 
} 

Antwort

2

Sie benötigen einen C++11 kompatible Compiler. Lesen Sie über union -s.

Im Allgemeinen müssen Sie explizit rufen Sie den Destruktor des alten Union-Members und dann den Konstruktor des neuen Union-Members. Eigentlich werden Sie besser tagged unions haben, mit dem tatsächlichen union einer Klasse anonym und Mitglied zu sein:

class TreeRecord; 

class TreeRecord { 
    bool hassinglechild; 
    typedef std::shared_ptr<TreeRecord> singlechild_type; 
    typedef std::vector<std::shared_ptr<TreeRecord>> children_type; 
    union { 
    singlechild_type child; // when hassinglechild is true 
    children_type children; // when hassinglechild is false 
    } 
    TreeRecord() : hassinglechild(true), child(nullptr) {}; 
    void set_child(TreeRecord&ch) { 
    if (!hassinglechild) { 
     children.~children_type(); 
     hassinglechild = true; 
     new (&child) singlechild_type(nullptr); 
    }; 
    child = ch; 
    } 
    /// other constructors and destructors skipped 
} 

Beachten Sie, dass ich ausdrücklich den destructor nenne ~children_type() dann bin ich mit der Platzierung new-explizit Aufruf des Erbauer für child.

Vergessen Sie nicht, die rule of five zu folgen.

Siehe auch boost::variant

BTW Ihr Code darauf hindeutet, dass Sie den Fall zu unterscheiden, wenn Sie ein child und den Fall haben, wenn Sie einen Ein-Element-Vektor children haben. Ist das freiwillig und sinnvoll?

PS. In einigen Sprachen, insbesondere Ocaml, sind getaggte Verbindungen (a.k.a. sum types) wesentlich einfacher zu definieren und zu implementieren als in C++ 11 .... Siehe wikipage von algebraic data types.

+0

Dachte nicht einmal darüber! Was meinst du mit "im Allgemeinen" –

+0

Dies ist die richtige Antwort, aber es wäre nett, wenn Sie es ausarbeiten. – mostruash

+0

Ich werde keinen ein elem Vektor haben. Ich benutze entweder Kind für 0 oder 1 Elem und ich wechsle zu Vektor für mehr Elems als 1. Oder habe ich einen Fehler gemacht ...? (Ich lese immer noch Ihren Code) Und ich verwende eine getaggte Union, nur nicht mit einem booleschen Bezeichner, sondern mit "size_t _childCount", was 0 oder 1 ist, wenn Child verwendet wird. –

1

Hinweis, die Verwendung von mehr als einem Element eines union ruft gleichzeitig undefiniertes Verhalten auf, z.

_nextGeneration.Children.push_back(_nextGeneration.Child); 

Wie @BasileStarynkevitch erwähnt, ist ein Weg, dies zu vermeiden, eine Vereinigung markiert ist.