001
24.01.2009, 06:42 Uhr
0xdeadbeef
Gott (Operator)
|
Das ist ein bisschen eine Designfrage. Ein Zeiger ist in der Tat eine Möglichkeit, insbesondere, wenn Daten ein großer und kompliziert anzulegender Datentyp ist. Allerdings gibt es ein paar Dinge dabei zu beachten - zum Beispiel musst du darauf achten, dass du dir kein POD-Struct baust, in dem die Zeiger nicht automatisch mit NULL initialisiert werden (am besten einen Konstruktor an RetValue dranbappen), und da data dann auf dem Heap liegt, musst du darauf achten, dass es auch wieder aufgeräumt wird (Analog RetValue einen Destruktor geben. Wie unterwartet).
Auch ist abhängig davon, wie du RetValue benutzen willst, eine Kopier- oder Bewegungssemantik erforderlich. Der Name des Typs lässt mich vermuten, dass du im Endeffekt so etwas wie
C++: |
RetValue klasse::methode();
|
vorhast, richtig? Das Problem hierbei ist, dass, wenn du Zeiger in RetValue hast, diese nicht automatisch tief kopiert werden, dementsprechend brauchst du einen Kopierkonstruktor. Zusammen sieht das etwa so aus:
C++: |
// hpp
struct RetValue { RetValue(); RetValue(RetValue const &); ~RetValue();
Daten *data; };
// cpp RetValue::RetValue() : data(0) { }
RetValue::RetValue(RetValue const &r) : data(0) { if(r.data) { data = new Daten(*r.data); } }
~RetValue() { delete data; }
|
Wenn Daten ein sehr komplexer Typ ist und das Kopieren sehr aufwändig ist, bietet sich dagegen eine Bewegungssemantik an (wie z.B. bei std::auto_ptr). In dem Fall entfällt der Kopierkonstruktor, und das ganze sieht dann etwa so aus:
C++: |
// hpp
struct RetValue { RetValue(); RetValue(RetValue &); ~RetValue();
Daten *data; };
// cpp RetValue::RetValue() : data(0) { }
RetValue::RetValue(RetValue &r) : data(r.data) { r.data = NULL; }
~RetValue() { delete data; }
|
Dieses Modell ist in seiner Funktionalität allerdings etwas eingeschränkt; zum Beispiel ist es so nicht möglich, RetValue in Containern der Standardbibliothek aufzubewahren. Es ist nur sinnvoll, wenn RetValue als nichts anderes als der Rückgabewert dieser Methode benutzt und data sofort da rausgeschält wird. Falls diese Einschränkung sich verbietet, aber Daten trotzdem zu komplex ist, um es dauernd hin- und herzukopieren, könnte sich ein Blick auf Boost.Smart_Ptr lohnen.
Andere Möglichkeiten drehen sich im Wesentlichen um die Verwendung von bools in verschiedenen Formen. Denkbar wäre zum Beispiel
C++: |
struct RetValue { RetValue();
Daten data; bool beschrieben; };
RetValue::RetValue() : beschrieben(false) { }
|
wobei RetValue.beschrieben halt in der Methode gesetzt wird, sofern data beschrieben wurde.
Konzeptionell empfiehlt sich hier Boost.Optional - das ist vermutlich der einfachste Weg. -- Einfachheit ist Voraussetzung für Zuverlässigkeit. -- Edsger Wybe Dijkstra |