2010-08-19 61 views
38

tôi muốn sử dụng chuỗi bên trong Liên minh. nếu tôi viết như sauTại sao trình biên dịch không cho phép std :: string bên trong union?

union U 
{ 
    int i; 
    float f; 
    string s; 
}; 

Trình biên dịch cho biết lỗi U :: S có hàm tạo bản sao.

Tôi đọc một số bài đăng khác để biết cách khác để giải quyết vấn đề này. Nhưng tôi muốn biết tại sao trình biên dịch không cho phép điều này ngay từ đầu?

EDIT: @KennyTM: Trong bất kỳ công đoàn nào, nếu thành viên được khởi tạo, những người khác sẽ có giá trị rác, nếu không có giá trị khởi tạo nào sẽ có giá trị rác. Tôi nghĩ rằng, công đoàn được gắn thẻ chỉ cung cấp một số tiện nghi để truy cập các giá trị hợp lệ từ Union. Câu hỏi của bạn: làm cách nào để bạn hoặc trình biên dịch viết một hàm tạo bản sao cho công đoàn ở trên mà không có thêm thông tin? sizeof (chuỗi) cho 4 byte. Dựa trên điều này, trình biên dịch có thể so sánh các kích thước thành viên khác và phân bổ phân bổ lớn nhất (4byte trong ví dụ của chúng tôi). Chiều dài chuỗi nội bộ không quan trọng vì nó sẽ được lưu trữ ở một vị trí riêng biệt. Để chuỗi có độ dài bất kỳ. Tất cả những gì mà Union phải biết là gọi hàm tạo bản sao lớp chuỗi với tham số chuỗi. Trong bất kỳ cách nào trình biên dịch tìm thấy rằng hàm tạo bản sao phải được gọi trong trường hợp bình thường, phương thức tương tự như được theo sau ngay cả khi chuỗi nằm trong Liên minh. Vì vậy, tôi nghĩ rằng trình biên dịch có thể làm như thế, phân bổ 4 byte. Sau đó, nếu bất kỳ chuỗi nào được gán cho s, thì lớp chuỗi sẽ xử lý việc phân bổ và sao chép chuỗi đó bằng cách sử dụng trình phân bổ của chính nó. Vì vậy, không có cơ hội tham nhũng bộ nhớ là tốt.

Chuỗi không tồn tại tại thời điểm phát triển Union trong trình biên dịch? Vì vậy, câu trả lời vẫn chưa rõ ràng với tôi. Là một joinee mới trong trang web này, nếu có gì sai, xin lỗi tôi.

+2

Không sử dụng 'union' khi bạn mới dùng C++,' union' là fiddle với bộ nhớ và đó là tên miền dành riêng cho các chuyên gia.Bạn cần phải tìm hiểu về C++ hướng đối tượng để hiểu tại sao điều này xảy ra và bạn thiếu quá nhiều khái niệm vào lúc này để hiểu bất kỳ câu trả lời nào (chúng có vẻ khó hiểu với bạn). Có sẵn các tài nguyên tuyệt vời và một số chủ đề trên trang này liệt kê chúng. –

+2

am mới cho trang web này, không phải C++. Dù sao .. Cảm ơn tất cả vì những nỗ lực và đầu vào của bạn. – bjskishore123

Trả lời

22

Hãy suy nghĩ về nó. Làm thế nào để trình biên dịch biết loại nào là trong liên minh?

Không. Các hoạt động cơ bản của một công đoàn về cơ bản là một diễn viên bitwise. Các hoạt động trên các giá trị chứa trong các công đoàn chỉ an toàn khi mỗi loại về cơ bản có thể được lấp đầy với rác. std::string không thể, vì điều đó sẽ dẫn đến hỏng bộ nhớ. Sử dụng boost::variant hoặc boost::any.

+0

@Matthieu: Vui lòng kiểm tra phiên bản đã chỉnh sửa của câu hỏi. Theo tôi, nó không nên gây ra tham nhũng bộ nhớ. – bjskishore123

+3

@bjskishore: sizeof (chuỗi) không thể di chuyển được. Ngoài ra, trình biên dịch không thể tìm thấy constructor/etc nào để gọi vì nó không biết loại nào nằm trong union. Tất cả các cuộc gọi chức năng phải được biết đến tại compiletime hoặc ảo, và một liên minh không phải là hằng số hay thời gian biên dịch. Vì vậy trình biên dịch không bao giờ biết phải làm gì. Ngoài ra, kích thước thực sự, thực sự không quan trọng - vấn đề là nó là một con trỏ. Nếu bạn lưu trữ một phao trong công đoàn sau đó truy cập một chuỗi, bạn sẽ có một phao nhị phân được coi là một con trỏ, mà gần như chắc chắn sẽ trỏ đến bộ nhớ không hợp lệ và sụp đổ chương trình của bạn. – Puppy

+0

@Puppy vui lòng sửa câu trả lời: mã OP được yêu cầu không hoạt động trong C++ 11 trở lên. –

13

Trong C++ 98/03, các thành viên của liên minh không thể có hàm tạo, hàm hủy, hàm thành viên ảo hoặc lớp cơ sở.

Vì vậy, về cơ bản, bạn có thể chỉ sử dụng được xây dựng trong các kiểu dữ liệu, hoặc PODs

Lưu ý rằng nó đang thay đổi trong C++ 0x: Unrestricted unions

union { 
    int z; 
    double w; 
    string s; // Illegal in C++98, legal in C++0x. 
}; 
+2

+1 để biết thông tin về C++ 0x. Cảm ơn bạn. – bjskishore123

45

Bởi vì việc có một lớp với một hàm tạo (/ copy) không tầm thường trong một công đoàn không có ý nghĩa. Giả sử chúng ta có

union U { 
    string x; 
    vector<int> y; 
}; 

U u; // <-- 

Nếu U là một cấu trúc, u.xu.y sẽ được khởi tạo một chuỗi rỗng và vector trống tương ứng. Nhưng các thành viên của một công đoàn chia sẻ cùng một địa chỉ. Vì vậy, nếu u.x được khởi tạo, u.y sẽ chứa dữ liệu không hợp lệ và ngược lại là vậy. Nếu cả hai không được khởi tạo thì chúng không thể được sử dụng. Trong mọi trường hợp, việc có các dữ liệu này trong một liên minh không thể được xử lý dễ dàng, vì vậy C++ 98 chọn từ chối điều này: (§ 9.5/1):

Một đối tượng của một lớp với một hàm tạo không tầm thường (12.1), một hàm tạo bản sao không tầm thường (12.8), một destructor không tầm thường (12.4) hoặc không nhỏ toán tử gán bản sao (13.5.3, 12.8) không thể là thành viên của một liên minh, cũng không thể là một mảng của các đối tượng như vậy.

Trong C++ 0x quy tắc này đã được nới lỏng (§ 9,5/2):

Tại hầu hết các thành viên một dữ liệu không tĩnh của một liên minh có thể có một cú đúp -hoặc-equal- initializer. [Lưu ý: nếu bất kỳ thành viên dữ liệu không tĩnh nào của công đoàn có một hàm tạo mặc định nhỏ (12.1), sao chép hàm khởi tạo (12.8), di chuyển hàm tạo (12.8), sao chép toán tử gán (12.8), di chuyển toán tử gán (12.8), hoặc destructor (12.4), chức năng thành viên tương ứng của công đoàn phải được người dùng cung cấp hoặc nó sẽ được xóa hoàn toàn (8.4.3) cho công đoàn. - lưu ý kết thúc]

nhưng vẫn không thể tạo (con) đúng/con destructors cho công đoàn, ví dụ: làm thế nào để bạn hoặc trình biên dịch viết một nhà xây dựng bản sao cho công đoàn ở trên mà không cần thêm thông tin? Để đảm bảo thành viên của công đoàn hoạt động, bạn cần một số tagged union và bạn cần xử lý việc xây dựng và phá hủy theo cách thủ công, ví dụ:

struct TU { 
    int type; 
    union { 
    int i; 
    float f; 
    std::string s; 
    } u; 

    TU(const TU& tu) : type(tu.type) { 
    switch (tu.type) { 
     case TU_STRING: new(&u.s)(tu.u.s); break; 
     case TU_INT: u.i = tu.u.i;  break; 
     case TU_FLOAT: u.f = tu.u.f;  break; 
    } 
    } 
    ~TU() { 
    if (tu.type == TU_STRING) 
     u.s.~string(); 
    } 
    ... 
}; 

Nhưng, như @DeadMG đã đề cập, đây đã được thực hiện như boost::variantorboost::any.

+0

giải thích tuyệt vời! – Chubsdad

+0

Định nghĩa của các nhà thầu tầm thường hoặc không tầm thường là gì? –

+0

@SiverSun tầm thường là những trình biên dịch tạo ra cho bạn, những thứ không tầm thường mà bạn viết mã. @ KennyTM, câu trả lời tuyệt vời thực sự! – FireAphis

7

Từ C++ đặc tả §9.5.1:

Một đối tượng của một lớp với một constructor không tầm thường, một constructor sao chép không tầm thường, một destructor không tầm thường, hoặc một tổ chức phi tầm thường toán tử gán bản sao không thể là thành viên của một công đoàn.

Lý do cho quy tắc này là trình biên dịch sẽ không bao giờ biết được đối tượng có thể ở bên trong công đoàn.

0

Rác được giới thiệu nếu bạn

  1. gán một chuỗi
  2. sau đó gán một int hoặc nổi
  3. sau đó một chuỗi lại

chuỗi quản lý bộ nhớ ở một nơi khác. Thông tin này rất có thể là một số con trỏ. Con trỏ này bị cắt xén khi gán int. Việc gán một chuỗi mới sẽ hủy chuỗi cũ, điều này là không thể.

Bước thứ hai nên hủy chuỗi, nhưng không biết, nếu có chuỗi.

Rõ ràng là họ đã tìm thấy giải pháp cho vấn đề này trong thời gian chờ đợi.

+0

Câu hỏi này đã hơn 5 tuổi. Một câu trả lời bây giờ cũng thực sự cũng phải đối phó với những thay đổi mà được thực hiện trong C++ 11 và làm thế nào 'std :: string' [có thể là một thành viên của một công đoàn bây giờ] (http://en.cppreference.com/w/cpp/ngôn ngữ/công đoàn). Có lẽ một số mã ví dụ sẽ là một ý tưởng hay. – Niall

+0

Một số người bị mắc kẹt với các phiên bản cũ. –

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