2015-07-29 17 views
5

Tôi đang cố gắng sử dụng một liên minh (C++) có một số biến không nguyên thủy, nhưng tôi đang cố gắng tạo ra destructor cho lớp đó. Như tôi đã đọc, không thể đoán biến nào của union được sử dụng nên không có destructor ngầm, và khi tôi sử dụng union này trên stack, các lỗi biên dịch mà destructor bị xóa. Các công đoàn như sau:Làm thế nào để viết destructor cho lớp giống như công đoàn

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

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

Biến type giữ những gì lĩnh vực công đoàn đang được sử dụng (được lựa chọn từ một enum), với mục đích đọc từ công đoàn và nó có thể được sử dụng để đoán giá trị gì nên bị xóa. Tôi đã thử một số cách tiếp cận khác nhau nhưng không ai trong số họ làm việc. Trước hết, chỉ cần thử phá hủy mặc định:

~LuaVariant() = default; 

Nó không hoạt động, như mặc định là ... xóa. Vì vậy, tôi đã cố gắng trao đổi các giá trị với một một sản phẩm nào, do đó nội dung sẽ bị xóa và sẽ không có vấn đề "rò rỉ" một giá trị rỗng:

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

Nhưng như tôi không phải là một bậc thầy của các đoàn thể, Tôi không biết nếu điều đó có thể gây ra vấn đề khác, chẳng hạn như bộ nhớ được phân bổ mà không bao giờ bị deallocated, hoặc một cái gì đó như thế. Chiến lược hoán đổi này có thể được sử dụng mà không có sai sót và các vấn đề?

+0

Thay vì lừa swap của bạn, bạn cần phải gọi một cách rõ ràng destructor. Mã của bạn không gọi destructors cho đối tượng trong union. Điều này có lẽ chỉ là một rò rỉ tài nguyên chứ không phải là UB, nhưng giải pháp sạch và chính xác nhất là gọi destructors. –

+0

* lớp giống công đoàn * là thuật ngữ Chuẩn cho một 'union', hoặc một lớp/struct chứa một liên kết ẩn danh. –

Trả lời

2

Nếu bạn muốn sử dụng std::string trong một liên minh trong C++ 11, bạn phải rõ ràng gọi hàm hủy của nó và vị trí mới để tạo cấu trúc đó. Ví dụ từ 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

Tốt, nhưng đó là một rắc rối lớn. Đôi khi tôi không biết trường nào đang được sử dụng (tôi chỉ cần đẩy và kéo đến/từ ngăn xếp Lua), vì vậy tôi sẽ cần một công tắc để tiêu diệt chúng, đúng không? Ngoài ra, có bất kỳ khuyết điểm nào đối với cách tiếp cận của tôi không? – ranisalt

+2

@ranisalt Bạn _have_ để biết bạn đang sử dụng trường nào, để biết bạn đang truy cập vào trường nào. Truy cập bất kỳ trường nào khác ngoài trường đang hoạt động là UB. – emlai

6

Nhóm này (giá trị công đoàn + phân biệt đối xử) được gọi là liên minh phân biệt đối xử. Bạn có thể gọi bất kỳ công trình xây dựng/phá hủy nào, bởi vì bản thân công đoàn không thể (nếu có thể, nó cũng có thể phân biệt đối với các loại được khởi tạo/không khởi tạo trong liên minh, và bạn sẽ không cần enum).

Code:

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

Tôi cần phải thêm một destructor trống trong công đoàn của tôi để làm cho nó hoạt động. Tui bỏ lỡ điều gì vậy? Tôi đã có một "giá trị :: ~ value() là một hàm bị xóa" nếu không – Julien

Các vấn đề liên quan