An meinem Arbeitsplatz sehe ich diesen Stil ausgiebig verwendet:
#include <iostream>
using namespace std;
class A
{
public:
A(int& thing) : m_thing(thing) {}
void printit() { cout << m_thing << endl; }
protected:
const int& m_thing; //usually would be more complex object
};
int main(int argc, char* argv[])
{
int myint = 5;
A myA(myint);
myA.printit();
return 0;
}
Gibt es einen Namen, der diese Redewendung beschreibt? Ich gehe davon aus, dass der möglicherweise große Aufwand beim Kopieren eines großen komplexen Objekts vermieden werden soll.
Ist das im Allgemeinen eine gute Praxis? Gibt es Fallstricke bei diesem Ansatz?
Antworten:
In UML heißt es Aggregation. Es unterscheidet sich von Zusammensetzung, dass das Mitgliedsobjekt nicht im Besitz des vorlegenden Klasse. In C ++ können Sie die Aggregation auf zwei verschiedene Arten implementieren, über Referenzen oder Zeiger.
Nein, das wäre ein wirklich schlechter Grund, dies zu nutzen. Der Hauptgrund für die Aggregation ist, dass das enthaltene Objekt nicht dem enthaltenen Objekt gehört und daher deren Lebensdauer nicht gebunden ist. Insbesondere muss die Lebensdauer des referenzierten Objekts die des referenzierenden Objekts überleben. Es wurde möglicherweise viel früher erstellt und lebt möglicherweise über das Ende der Lebensdauer des Containers hinaus. Außerdem wird der Status des referenzierten Objekts nicht von der Klasse gesteuert, sondern kann sich extern ändern. Ist dies nicht
const
der Fall, kann die Klasse den Status eines Objekts ändern, das außerhalb des Objekts lebt.Es ist ein Designwerkzeug. In einigen Fällen ist es eine gute Idee, in anderen nicht. Die häufigste Gefahr besteht darin, dass die Lebensdauer des Objekts, das die Referenz enthält, niemals die Lebensdauer des referenzierten Objekts überschreiten darf. Wenn das einschließende Objekt die Referenz verwendet, nachdem das referenzierte Objekt zerstört wurde, liegt ein undefiniertes Verhalten vor. Im Allgemeinen ist es besser, die Komposition der Aggregation vorzuziehen, aber wenn Sie sie brauchen, ist sie genauso gut wie jedes andere Werkzeug.
quelle
to prevent the possibly large overhead of copying a big complex object?
Es wird Abhängigkeitsinjektion über Konstruktorinjektion genannt : Klasse ruft
A
die Abhängigkeit als Argument für ihren Konstruktor ab und speichert den Verweis auf abhängige Klasse als private Variable.Es gibt eine interessante Einführung auf Wikipedia .
Aus Gründen der Konstanz würde ich schreiben:
using T = int; class A { public: A(const T &thing) : m_thing(thing) {} // ... private: const T &m_thing; };
Ein Problem mit dieser Klasse ist jedoch, dass sie Verweise auf temporäre Objekte akzeptiert:
T t; A a1{t}; // this is ok, but... A a2{T()}; // ... this is BAD.
Es ist besser hinzuzufügen (erfordert mindestens C ++ 11):
class A { public: A(const T &thing) : m_thing(thing) {} A(const T &&) = delete; // prevents rvalue binding // ... private: const T &m_thing; };
Wie auch immer, wenn Sie den Konstruktor ändern:class A { public: A(const T *thing) : m_thing(*thing) { assert(thing); } // ... private: const T &m_thing; };
Es ist so gut wie garantiert, dass Sie keinen Zeiger auf eine temporäre haben .
Da der Konstruktor einen Zeiger verwendet, ist es für Benutzer klarer,A
dass sie auf die Lebensdauer des übergebenen Objekts achten müssen.Etwas verwandte Themen sind:
quelle
Es gibt keinen Namen für diese Verwendung, sie wird einfach als "Referenz als Klassenmitglied" bezeichnet .
Ja und auch Szenarien, in denen Sie die Lebensdauer eines Objekts einem anderen Objekt zuordnen möchten.
Hängt von Ihrer Nutzung ab. Die Verwendung einer beliebigen Sprachfunktion entspricht der Auswahl von Pferden für Kurse . Es ist wichtig zu beachten, dass jede ( fast alle ) Sprachfunktion vorhanden ist, da sie in einigen Szenarien nützlich ist.
Bei der Verwendung von Referenzen als Klassenmitglieder sind einige wichtige Punkte zu beachten:
operator=()
und Sie müssen selbst eine bereitstellen. Es ist umständlich zu bestimmen, welche Maßnahmen Ihr=
Bediener in einem solchen Fall ergreifen soll. Im Grunde genommen wird Ihre Klasse nicht mehr zuweisbar .NULL
, um auf ein anderes Objekt zu verweisen. Wenn Sie erneut setzen müssen, ist dies mit einer Referenz nicht möglich, wie im Fall eines Zeigers.Für die meisten praktischen Zwecke (es sei denn, Sie sind wirklich besorgt über eine hohe Speichernutzung aufgrund der Elementgröße) sollte es ausreichen, nur eine Elementinstanz anstelle eines Zeigers oder Referenzelements zu haben. Dies erspart Ihnen eine Menge Sorgen über andere Probleme, die Referenz- / Zeigerelemente auf Kosten der zusätzlichen Speichernutzung mit sich bringen.
Wenn Sie einen Zeiger verwenden müssen, stellen Sie sicher, dass Sie einen intelligenten Zeiger anstelle eines Rohzeigers verwenden. Das würde Ihr Leben mit Zeigern viel einfacher machen.
quelle
C ++ bietet einen guten Mechanismus zum Verwalten der Lebensdauer eines Objekts durch Klassen- / Strukturkonstrukte. Dies ist eine der besten Funktionen von C ++ gegenüber anderen Sprachen.
Wenn Sie Mitgliedsvariablen durch ref oder Zeiger verfügbar machen, verletzt dies im Prinzip die Kapselung. Diese Redewendung ermöglicht es dem Verbraucher der Klasse, den Zustand eines Objekts von A zu ändern, ohne dass es (A) Kenntnis oder Kontrolle darüber hat. Es ermöglicht dem Verbraucher auch, über die Lebensdauer des Objekts von A hinaus an einem Verweis / Zeiger auf den internen Zustand von A festzuhalten. Dies ist ein schlechtes Design. Stattdessen könnte die Klasse so umgestaltet werden, dass sie einen Verweis / Zeiger auf das gemeinsam genutzte Objekt enthält (nicht dessen Eigentümer), und diese könnten mithilfe des Konstruktors festgelegt werden (Mandate the Life Time Rules). Die Klasse des gemeinsam genutzten Objekts kann so konzipiert sein, dass sie je nach Fall Multithreading / Parallelität unterstützt.
quelle
Mitgliederreferenzen werden normalerweise als schlecht angesehen. Sie machen das Leben schwer im Vergleich zu Mitgliedszeigern. Aber es ist weder besonders ungewöhnlich noch eine spezielle Redewendung oder Sache. Es ist nur Aliasing.
quelle