C ++のunionはかなりクールだと思います。人々は通常、unionインスタンスの値を「その場で」変更したいというユースケースのみを考えているようです(これは、メモリを節約するか、疑わしい変換を実行するためにのみ役立つようです)。
実際、ユニオンインスタンスの値を変更しない場合でも、ユニオンはソフトウェアエンジニアリングツールとして非常に役立ちます。
ユースケース1:カメレオン
ユニオンを使用すると、1つの金種の下で多数の任意のクラスを再グループ化できます。これは、基本クラスとその派生クラスの場合との類似点がないわけではありません。ただし、変更点は、特定のユニオンインスタンスで実行できることと実行できないことです。
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?
}
プログラマーは、特定の共用体インスタンスを使用する場合、そのコンテンツのタイプを特定する必要があるようです。f
上記の機能の場合です。ただし、g
上記の場合のように、渡された引数として関数が共用体インスタンスを受け取る場合、関数はそれをどうするかわかりません。同じことがunionインスタンスを返す関数にも当てはまります。参照h
:呼び出し元は内部が何であるかをどのようにして知るのですか?
unionインスタンスが引数または戻り値として渡されない場合、プログラマーがコンテンツを変更することを選択したときに興奮のスパイクが発生し、非常に単調な人生を送ることになります。
Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;
そしてそれが最も人気のある(非)ユニオンの使用例です。もう1つの使用例は、共用体インスタンスがその型を通知する何かを伴う場合です。
使用例2:「はじめまして」object
からClass
プログラマがunionインスタンスと型記述子を常にペアにすることを選択したとします(そのようなオブジェクトの実装を想像するのは読者の裁量に任せます)。プログラマがメモリを節約することを望んでおり、型記述子のサイズが共用体のサイズに関して無視できない場合、これは共用体自体の目的を無効にします。しかし、unionのインスタンスが引数または戻り値として渡され、呼び出し先または呼び出し元が何が入っているかを知らないことが重要であるとしましょう。
次に、プログラマは switch
制御フローステートメントを記述して、ブルースウェインに木製の棒またはそれと同等のものを区別させる必要があります。ユニオンにコンテンツが2種類しかない場合でもそれほど悪くはありませんが、明らかに、ユニオンはもうスケーリングしません。
ユースケース3:
ISO C ++標準の勧告の作成者が2008年にそれを戻したように、
多くの重要な問題ドメインには、多数のオブジェクトまたは限られたメモリリソースのいずれかが必要です。これらの状況では、スペースを節約することが非常に重要であり、多くの場合、ユニオンはそのための完璧な方法です。実際、一般的なユースケースは、組合がその有効期間中にアクティブメンバーを変更しない状況です。メンバーを1つだけ含む構造体であるかのように、構築、コピー、破棄できます。これの典型的なアプリケーションは、動的に割り当てられない無関係なタイプの異種のコレクションを作成することです(おそらく、それらはマップ内でインプレースで構築されるか、配列のメンバーです)。
そして今、UMLクラス図の例:
平易な英語の状況:クラスAのオブジェクトは、B1、...、Bn、および各タイプの多くても1つのクラスのオブジェクトを持つことができます。nはかなり大きな数で、たとえば少なくとも10です。
次のようにフィールド(データメンバー)をAに追加したくありません。
private:
B1 b1;
.
.
.
Bn bn;
nが変化する可能性があるため(Bxクラスをミックスに追加する場合がある)、これによりコンストラクターが混乱し、Aオブジェクトが多くのスペースを占有するためです。
キャストを使用したオブジェクトvoid*
へのポインターの奇抜なコンテナーを使用してBx
それらを取得することもできますが、それはあいまいでCスタイルですが、さらに重要なことに、動的に割り当てられた多くのオブジェクトのライフタイムを管理することができます。
代わりに、できることは次のとおりです。
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
};
次に、からユニオンインスタンスのコンテンツを取得するには、data
などを使用a.get(TYPE_B2).b2
します。ここa
で、はクラスA
インスタンスです。
ユニオンはC ++ 11では制限がないため、これはさらに強力です。詳細については、上記のリンク先のドキュメントまたはこの記事を参照してください。