2015-07-29 49 views
5

Próbuję użyć unii (C++), która ma pewne zmienne nieprymujące, ale utknąłem próbując utworzyć destruktor dla tej klasy. Jak już przeczytałem, nie można zgadnąć, jaka zmienna związku jest używana, więc nie ma niejawnego destruktora, a ponieważ używam tego związku na stosie, błędy kompilatora, który usuwa destruktor. Unia jest w następujący sposób:Jak pisać destruktor dla klasy podobnej do unii

struct LuaVariant { 
    LuaVariant() : type(VARIANT_NONE) { } 

    LuaVariantType_t type; 
    union { 
     std::string text; 
     Position pos; 
     uint32_t number; 
    }; 
}; 

Zmienna type posiada co pole unii jest używany (wybrany z wyliczenia), w celu odczytu z unii i może być używany do odgadnąć, co wartość powinna być usunięte. Próbowałem różnych podejść, ale żaden z nich nie działał. Po pierwsze, po prostu wypróbowałem domyślny destruktor:

~LuaVariant() = default; 

To nie działało, ponieważ domyślnie jest ... usunięte. Tak, próbowałem zamianę wartości z pustym drugim, tak że zawartość zostanie skasowana i nie byłoby problemu „przecieka” pusty wartość:

~LuaVariant() { 
    switch (type) { 
     case VARIANT_POSITION: 
     case VARIANT_TARGETPOSITION: { 
      Position p; 
      std::swap(p, pos); 
      break; 
     } 
     case VARIANT_STRING: { 
      std::string s; 
      std::swap(s, text); 
      break; 
     } 
     default: 
      number = 0; 
      break; 
    } 
}; 

Ale nie jestem mistrzem związkami, Nie wiem, czy może to spowodować inne problemy, takie jak przydzielona pamięć, która nigdy nie zostanie zwolniona, czy coś w tym stylu. Czy ta strategia wymiany może być używana bez wad i problemów?

+0

zamiast swojego wymiany sztuczki, trzeba jawnie wywołać destruktor. Twój kod nie wywołuje destruktorów obiektu w unii. Jest to prawdopodobnie tylko wyciek zasobów, a nie UB, ale najczystszym i właściwym rozwiązaniem jest wywołanie destruktorów. –

+0

* klasa podobna do unii * jest terminem standardowym dla "union" lub klasy/struct zawierającej anonimowy union. –

Odpowiedz

2

Jeśli chcesz używać std::string w unii w C++ 11, musisz jawnie wywołać jego destruktor, a miejsce docelowe nowe, aby je skonstruować. Przykład z cppreference.com:

#include <iostream> 
#include <string> 
#include <vector> 
union S { 
    std::string str; 
    std::vector<int> vec; 
    ~S() {} // needs to know which member is active, only possible in union-like class 
}; // the whole union occupies max(sizeof(string), sizeof(vector<int>)) 

int main() 
{ 
    S s = {"Hello, world"}; 
    // at this point, reading from s.vec is UB 
    std::cout << "s.str = " << s.str << '\n'; 
    s.str.~basic_string<char>(); 
    new (&s.vec) std::vector<int>; 
    // now, s.vec is the active member of the union 
    s.vec.push_back(10); 
    std::cout << s.vec.size() << '\n'; 
    s.vec.~vector<int>(); 
} 
+0

Dobrze, ale to jest duży problem. Czasem nie wiem, które z pól jest używane (po prostu pcham i ciągnę do/ze stosu Lua), więc i tak potrzebowałbym przełącznika, żeby je zniszczyć, prawda? Czy są jakieś minusy mojego podejścia? – ranisalt

+2

@ranisalt Użytkownik ma możliwość sprawdzenia, z których pól korzysta, aby wiedzieć, do którego z nich można uzyskać dostęp. Dostęp do dowolnego pola innego niż aktywny jest UB. – emlai

6

Ugrupowanie (związek + wyliczenia wartości odróżniania rodzaju) nazywa się rozróżnić związku.

Od ciebie zależy, czy wywołasz jakąkolwiek konstrukcję/zniszczenie, ponieważ związek nie może (jeśli to możliwe, byłby w stanie rozróżnić dla inicjowanych/niewinnych typów w ramach związku, a ty byś nie potrzebował enum).

Kod:

class LuaVariant // no public access to the raw union 
{ 
public: 
    LuaVariant() : type(VARIANT_NONE) { } 
    ~LuaVariant() { destroy_value(); } 

    void text(std::string value) // here's a setter example 
    { 
     using std::string; 
     destroy_value(); 
     type = VARIANT_TEXT; 
     new (&value.text) string{ std::move(value) }; 
    } 
private: 

    void destroy_value() 
    { 
     using std::string; 
     switch(type) 
     { 
     case VARIANT_TEXT: 
      (&value.text)->string::~string(); 
      break; 
     case VARIANT_POSITION: 
      (&value.pos)->Position::~Position(); 
      break; 
     case VARIANT_NUMBER: 
      value.number = 0; 
      break; 
     default: 
      break; 
     } 
    } 

    LuaVariantType_t type; 
    union { 
     std::string text; 
     Position pos; 
     uint32_t number; 
    } value; 
}; 
+1

Potrzebowałem dodać pusty destruktor w moim zjednoczeniu, aby go uruchomić. Czy czegoś brakuje? W przeciwnym razie miałem "value :: ~ value() is a deleted function" – Julien