Ich finde C ++ - Gewerkschaften ziemlich cool. Es scheint, dass die Leute normalerweise nur an den Anwendungsfall denken, in dem man den Wert einer Union-Instanz "an Ort und Stelle" ändern möchte (was anscheinend nur dazu dient, Speicherplatz zu sparen oder zweifelhafte Konvertierungen durchzuführen).
In der Tat können Gewerkschaften als Software-Engineering-Tool von großer Bedeutung sein, selbst wenn Sie den Wert einer Gewerkschaftsinstanz niemals ändern .
Anwendungsfall 1: das Chamäleon
Mit Gewerkschaften können Sie eine Reihe beliebiger Klassen unter einer Bezeichnung zusammenfassen, was nicht ohne Ähnlichkeiten mit dem Fall einer Basisklasse und ihren abgeleiteten Klassen ist. Was sich jedoch ändert, ist, was Sie mit einer bestimmten Union-Instanz tun können und was nicht:
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?
}
Es scheint, dass der Programmierer sicher sein muss, welche Art von Inhalt eine bestimmte Union-Instanz hat, wenn er sie verwenden möchte. Dies ist in der f
obigen Funktion der Fall . Wenn eine Funktion jedoch eine Union-Instanz als übergebenes Argument erhalten würde, wie dies g
oben der Fall ist , weiß sie nicht, was sie damit tun soll. Gleiches gilt für Funktionen, die eine Union-Instanz zurückgeben, sieheh
: Woher weiß der Aufrufer, was sich darin befindet?
Wenn eine Union-Instanz niemals als Argument oder als Rückgabewert übergeben wird, hat sie zwangsläufig ein sehr eintöniges Leben mit Aufregung, wenn der Programmierer seinen Inhalt ändert:
Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;
Und das ist der (un) beliebteste Anwendungsfall von Gewerkschaften. Ein weiterer Anwendungsfall ist, wenn eine Union-Instanz etwas enthält, das Ihnen den Typ angibt.
Anwendungsfall 2: "Schön, Sie kennenzulernen, ich bin object
von Class
"
Angenommen, ein Programmierer hat sich dafür entschieden, eine Union-Instanz immer mit einem Typdeskriptor zu koppeln (ich überlasse es dem Ermessen des Lesers, sich eine Implementierung für ein solches Objekt vorzustellen). Dies macht den Zweck der Union selbst zunichte, wenn der Programmierer Speicher sparen möchte und die Größe des Typdeskriptors in Bezug auf die der Union nicht vernachlässigbar ist. Nehmen wir jedoch an, dass es entscheidend ist, dass die Union-Instanz als Argument oder als Rückgabewert übergeben wird, wenn der Angerufene oder Anrufer nicht weiß, was sich darin befindet.
Dann muss der Programmierer a schreiben switch
Kontrollflussanweisung , um Bruce Wayne von einem Holzstab oder etwas Ähnlichem zu unterscheiden. Es ist nicht schlecht, wenn es nur zwei Arten von Inhalten in der Union gibt, aber offensichtlich skaliert die Union nicht mehr.
Anwendungsfall 3:
Wie die Autoren einer Empfehlung für den ISO C ++ - Standard bereits 2008 formulierten,
Viele wichtige Problemdomänen erfordern entweder eine große Anzahl von Objekten oder begrenzte Speicherressourcen. In diesen Situationen ist es sehr wichtig, Platz zu sparen, und eine Gewerkschaft ist oft der perfekte Weg, dies zu tun. In der Tat ist ein häufiger Anwendungsfall die Situation, in der eine Gewerkschaft während ihres Lebens niemals ihr aktives Mitglied wechselt. Es kann so konstruiert, kopiert und zerstört werden, als wäre es eine Struktur, die nur ein Mitglied enthält. Eine typische Anwendung hierfür wäre die Erstellung einer heterogenen Sammlung nicht verwandter Typen, die nicht dynamisch zugewiesen werden (möglicherweise sind sie direkt in einer Karte oder in Mitgliedern eines Arrays erstellt).
Und nun ein Beispiel mit einem UML-Klassendiagramm:
Die Situation im Klartext: Ein Objekt der Klasse A kann Objekte jeder Klasse unter B1, ..., Bn und höchstens eines von jedem Typ haben, wobei n eine ziemlich große Zahl ist, sagen wir mindestens 10.
Wir möchten A keine Felder (Datenelemente) hinzufügen, wie folgt:
private:
B1 b1;
.
.
.
Bn bn;
weil n variieren kann (wir möchten möglicherweise Bx-Klassen zum Mix hinzufügen) und weil dies ein Durcheinander mit Konstruktoren verursachen würde und weil A-Objekte viel Platz beanspruchen würden.
Wir könnten einen verrückten Container mit void*
Zeigern auf Bx
Objekte mit Casts verwenden, um sie abzurufen, aber das ist flüchtig und so im C-Stil ... aber was noch wichtiger ist, das würde uns die Lebensdauer vieler dynamisch zugeordneter Objekte überlassen, die verwaltet werden müssen.
Stattdessen kann Folgendes getan werden:
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
};
Um den Inhalt einer Union-Instanz abzurufen data
, verwenden Sie a.get(TYPE_B2).b2
und dergleichen, wobei a
sich eine Klasseninstanz befindet A
.
Dies ist umso leistungsfähiger, als die Gewerkschaften in C ++ 11 uneingeschränkt sind. Weitere Informationen finden Sie im oben verlinkten Dokument oder in diesem Artikel .