Tôi thấy công đoàn C ++ khá tuyệt. Dường như mọi người thường chỉ nghĩ đến trường hợp sử dụng khi người ta muốn thay đổi giá trị của một thể hiện liên minh "tại chỗ" (có vẻ như chỉ phục vụ để lưu bộ nhớ hoặc thực hiện chuyển đổi đáng ngờ).
Trong thực tế, các công đoàn có thể có sức mạnh lớn như một công cụ kỹ thuật phần mềm, ngay cả khi bạn không bao giờ thay đổi giá trị của bất kỳ trường hợp liên minh nào .
Trường hợp sử dụng 1: tắc kè hoa
Với các hiệp hội, bạn có thể tập hợp lại một số lớp tùy ý theo một mệnh giá, không có sự tương đồng với trường hợp của lớp cơ sở và các lớp dẫn xuất của nó. Tuy nhiên, những thay đổi là những gì bạn có thể và không thể làm với một thể hiện liên minh cụ thể:
struct Batman;
struct BaseballBat;
union Bat
{
Batman brucewayne;
BaseballBat club;
};
ReturnType1 f(void)
{
BaseballBat bb = {/* */};
Bat b;
b.club = bb;
// do something with b.club
}
ReturnType2 g(Bat& b)
{
// do something with b, but how do we know what's inside?
}
Bat returnsBat(void);
ReturnType3 h(void)
{
Bat b = returnsBat();
// do something with b, but how do we know what's inside?
}
Có vẻ như lập trình viên phải chắc chắn về loại nội dung của một thể hiện liên minh nhất định khi anh ta muốn sử dụng nó. Đây là trường hợp trong chức năng f
trên. Tuy nhiên, nếu một hàm được nhận một thể hiện hợp nhất như là một đối số được thông qua, như trường hợp g
ở trên, thì nó sẽ không biết phải làm gì với nó. Điều tương tự cũng áp dụng cho các hàm trả về một thể hiện hợp nhất, xemh
: làm thế nào để người gọi biết những gì bên trong?
Nếu một thể hiện liên minh không bao giờ được thông qua như một đối số hoặc như một giá trị trả về, thì nó chắc chắn sẽ có một cuộc sống rất đơn điệu, với sự phấn khích khi lập trình viên chọn thay đổi nội dung của nó:
Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;
Và đó là trường hợp sử dụng phổ biến nhất (chưa) của các công đoàn. Một trường hợp sử dụng khác là khi một thể hiện hợp nhất đi kèm với một cái gì đó cho bạn biết loại của nó.
Trường hợp sử dụng 2: "Rất vui được gặp bạn, tôi object
, từClass
"
Giả sử một lập trình viên được bầu để luôn ghép một thể hiện hợp nhất với một mô tả kiểu (tôi sẽ để nó theo ý của người đọc để tưởng tượng việc thực hiện cho một đối tượng như vậy). Điều này đánh bại mục đích của chính liên minh nếu điều mà lập trình viên muốn là tiết kiệm bộ nhớ và kích thước của bộ mô tả kiểu không đáng kể so với liên kết. Nhưng hãy giả sử rằng điều quan trọng là thể hiện liên minh có thể được thông qua dưới dạng đối số hoặc là giá trị trả về với callee hoặc người gọi không biết những gì bên trong.
Sau đó, lập trình viên phải viết một switch
tuyên bố dòng điều khiển để nói với Bruce Wayne ngoài một thanh gỗ, hoặc một cái gì đó tương đương. Nó không quá tệ khi chỉ có hai loại nội dung trong liên minh nhưng rõ ràng, công đoàn không còn quy mô nữa.
Trường hợp sử dụng 3:
Như các tác giả của một khuyến nghị cho Tiêu chuẩn ISO C ++ đã đưa nó trở lại vào năm 2008,
Nhiều miền vấn đề quan trọng yêu cầu số lượng lớn đối tượng hoặc tài nguyên bộ nhớ hạn chế. Trong những tình huống này, việc bảo tồn không gian là rất quan trọng và liên minh thường là một cách hoàn hảo để làm điều đó. Trong thực tế, một trường hợp sử dụng phổ biến là tình huống mà một công đoàn không bao giờ thay đổi thành viên tích cực của mình trong suốt cuộc đời. Nó có thể được xây dựng, sao chép và phá hủy như thể nó là một cấu trúc chỉ chứa một thành viên. Một ứng dụng điển hình của việc này sẽ là tạo ra một bộ sưu tập các loại không liên quan không được phân bổ động (có lẽ chúng được đặt tại chỗ trong bản đồ hoặc các thành viên của một mảng).
Và bây giờ, một ví dụ, với sơ đồ lớp UML:
Tình huống bằng tiếng Anh đơn giản: một đối tượng của lớp A có thể có các đối tượng thuộc bất kỳ lớp nào trong số B1, ..., Bn và nhiều nhất là một trong mỗi loại, với n là một số khá lớn, ít nhất là 10.
Chúng tôi không muốn thêm các trường (thành viên dữ liệu) vào A như vậy:
private:
B1 b1;
.
.
.
Bn bn;
bởi vì n có thể khác nhau (chúng ta có thể muốn thêm các lớp Bx vào hỗn hợp) và bởi vì điều này sẽ gây ra sự lộn xộn với các hàm tạo và vì các đối tượng A sẽ chiếm rất nhiều không gian.
Chúng ta có thể sử dụng một thùng chứa void*
con trỏ lập dị đểBx
các đối tượng có phôi để lấy chúng, nhưng đó là kiểu chạy trốn và theo kiểu C ... nhưng quan trọng hơn là điều đó sẽ khiến chúng ta có nhiều thời gian để quản lý các đối tượng được phân bổ động.
Thay vào đó, những gì có thể được thực hiện là đây:
union Bee
{
B1 b1;
.
.
.
Bn bn;
};
enum BeesTypes { TYPE_B1, ..., TYPE_BN };
class A
{
private:
std::unordered_map<int, Bee> data; // C++11, otherwise use std::map
public:
Bee get(int); // the implementation is obvious: get from the unordered map
};
Sau đó, để lấy nội dung của một thể hiện liên minh từ đó data
, bạn sử dụng a.get(TYPE_B2).b2
và lượt thích, a
một lớp học ở đâuA
thể hiện của .
Đây là tất cả những gì mạnh mẽ hơn vì các công đoàn không bị hạn chế trong C ++ 11. Xem tài liệu được liên kết đến ở trên hoặc bài viết này để biết chi tiết.