2016-01-03 26 views
7

Do tej pory tylko związki stosowane do przechowywania każde z państw człon A lub B.
ja teraz znajduję się w przypadku, gdy chcę zmiany zużyty zarejestrował podczas wykonywania.
C++ Unia

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

Mój obecny Wykorzystanie:

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++; 
    } 
} 

Nowa realizacja (spróbuj):

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++; 
    } 
} 

Odpowiedz

2

Trzeba kompilatora zgodnego C++11. Przeczytaj o union -s.

Ogólnie rzecz biorąc, musisz jawnie wywołać destruktora starego członka związku, a następnie konstruktora nowego członka związku. Właściwie, to lepiej mieć tagged unions z rzeczywistą union anonimowości i członek pewnej klasy:

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 
} 

Zauważ, że jestem jawne wywołanie destruktora ~children_type() następnie używam umieszczenie new do wyraźnie połączenia z konstruktor dla child.

Nie zapomnij postępować zgodnie z rule of five.

Zobacz także boost::variant

BTW kod sugeruje, że można odróżnić przypadek, gdy masz child i przypadek, gdy masz jeden element wektora z children. Czy to jest dobrowolne i znaczące?

PS. W niektórych językach, w szczególności Ocaml, oznaczane związki (a.k.a. rodzaje sum) są znacznie łatwiejsze do zdefiniowania i implementacji niż w C++ 11 .... Zobacz stronę wiki z algebraic data types.

+0

Nawet o tym nie pomyślał! Co masz na myśli przez "ogólnie"? –

+0

To jest poprawna odpowiedź, ale byłoby miło, gdybyś ją rozwinął. – mostruash

+0

Nie będę miał jednego wektora elem. Używam Child dla 0 lub 1 elem i przełączam się na wektor dla więcej niż 1 elem. Czy popełniłem błąd ...? (Wciąż czytam twój kod) I używam oznaczonego związku, po prostu nie z boolowskim identyfikatorem, ale z "size_t _childCount", który wynosi 0 lub 1, gdy dziecko jest używane. –

1

Uwaga: użycie więcej niż jednego elementu obiektu union powoduje jednoczesne wywołanie niezdefiniowanego zachowania, np.

_nextGeneration.Children.push_back(_nextGeneration.Child); 

Jak wspomniano o @BasileStarynkevitch, jednym ze sposobów jest uniknięcie oznaczonego związku.