Saya menemukan serikat C ++ cukup keren. Tampaknya orang biasanya hanya memikirkan use case di mana seseorang ingin mengubah nilai instance serikat "di tempat" (yang, tampaknya, berfungsi hanya untuk menghemat memori atau melakukan konversi yang meragukan).
Faktanya, serikat pekerja dapat memiliki kekuatan besar sebagai alat rekayasa perangkat lunak, bahkan ketika Anda tidak pernah mengubah nilai instance serikat apa pun .
Gunakan case 1: bunglon
Dengan serikat pekerja, Anda dapat mengelompokkan kembali sejumlah kelas sewenang-wenang di bawah satu denominasi, yang bukan tanpa kesamaan dengan kasus kelas dasar dan kelas turunannya. Namun, perubahan apa yang bisa dan tidak bisa Anda lakukan dengan contoh serikat pekerja tertentu:
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?
}
Tampaknya programmer harus yakin dengan jenis konten instance serikat tertentu ketika dia ingin menggunakannya. Ini adalah kasus dalam fungsi di f
atas. Namun, jika suatu fungsi menerima instance serikat sebagai argumen yang disahkan, seperti halnya dengan di g
atas, maka tidak akan tahu apa yang harus dilakukan dengan itu. Hal yang sama berlaku untuk fungsi mengembalikan instance serikat, lihat h
: bagaimana penelepon tahu apa yang ada di dalamnya?
Jika instance serikat tidak pernah dianggap sebagai argumen atau sebagai nilai balik, maka itu pasti memiliki kehidupan yang sangat monoton, dengan lonjakan kegembiraan ketika programmer memilih untuk mengubah kontennya:
Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;
Dan itulah kasus penggunaan serikat yang paling populer. Kasus penggunaan lain adalah ketika contoh serikat datang dengan sesuatu yang memberitahu Anda jenisnya.
Gunakan case 2: "Senang bertemu Anda, saya object
, dari Class
"
Misalkan seorang programmer terpilih untuk selalu memasangkan instance union dengan deskriptor tipe (saya akan menyerahkannya pada kebijaksanaan pembaca untuk membayangkan implementasi untuk satu objek seperti itu). Ini mengalahkan tujuan serikat itu sendiri jika apa yang diinginkan programmer adalah untuk menghemat memori dan bahwa ukuran deskriptor jenis tidak dapat diabaikan sehubungan dengan serikat. Tetapi mari kita anggap bahwa sangat penting bahwa contoh serikat pekerja dapat disahkan sebagai argumen atau sebagai nilai pengembalian dengan callee atau penelepon yang tidak mengetahui apa yang ada di dalamnya.
Kemudian programmer harus menulis switch
pernyataan aliran kontrol untuk memberi tahu Bruce Wayne terpisah dari tongkat kayu, atau sesuatu yang setara. Ini tidak terlalu buruk ketika hanya ada dua jenis konten di serikat tetapi jelas, serikat tidak skala lagi.
Gunakan case 3:
Sebagai penulis rekomendasi untuk Standar ISO C ++ mengembalikannya pada tahun 2008,
Banyak domain masalah penting memerlukan sejumlah besar objek atau sumber daya memori terbatas. Dalam situasi ini, menghemat ruang sangat penting, dan penyatuan sering kali merupakan cara yang sempurna untuk melakukan itu. Faktanya, kasus penggunaan umum adalah situasi di mana serikat pekerja tidak pernah mengubah anggota aktifnya selama masa hidupnya. Itu dapat dibangun, disalin, dan dihancurkan seolah-olah itu adalah struct yang hanya mengandung satu anggota. Aplikasi khas ini adalah untuk membuat koleksi heterogen dari jenis yang tidak terkait yang tidak dialokasikan secara dinamis (mungkin mereka di tempat dibangun di peta, atau anggota array).
Dan sekarang, sebuah contoh, dengan diagram kelas UML:
Situasi dalam bahasa Inggris biasa: objek kelas A dapat memiliki objek kelas apa pun di antara B1, ..., Bn, dan paling banyak satu dari setiap tipe, dengan n menjadi angka yang cukup besar, katakan setidaknya 10.
Kami tidak ingin menambahkan bidang (anggota data) ke A seperti:
private:
B1 b1;
.
.
.
Bn bn;
karena n mungkin bervariasi (kami mungkin ingin menambahkan kelas Bx ke dalam campuran), dan karena ini akan menyebabkan kekacauan dengan konstruktor dan karena objek A akan memakan banyak ruang.
Kita bisa menggunakan wadah aneh dari void*
pointer ke Bx
objek dengan gips untuk mengambilnya, tapi itu gaya C dan jelek ... tapi yang lebih penting itu akan meninggalkan kita dengan masa hidup dari banyak objek yang dialokasikan secara dinamis untuk dikelola.
Sebaliknya, yang bisa dilakukan adalah ini:
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
};
Kemudian, untuk mendapatkan konten instance serikat data
, Anda menggunakan a.get(TYPE_B2).b2
dan suka, di mana instance a
kelas A
.
Ini semakin kuat karena serikat pekerja tidak dibatasi dalam C ++ 11. Lihat dokumen yang ditautkan ke atas atau artikel ini untuk detailnya.