Giving Kind Zugang zu Eltern Mitglied durch Bezugnahme - ist es OK?
-
20-08-2019 - |
Frage
C ++ newbie Frage. Bitte überprüfen Ich mache es richtig.
Ich habe eine globale Anwendungsklasse ist es kleine Kinder Laichen und ich brauche die Kinder einige der Anwendungsmöglichkeiten Zugang zu geben. Also habe ich beschlossen, sie zu Kindern durch Verweis übergeben.
Getestet habe ich die Idee, wie unten gezeigt. Es scheint gut zu funktionieren. Ich wollte nur sicherstellen, dass ich nicht etwas Gefährliches tun. da sein könnte Fallstricke ich übersehen?
Dad schafft Kinder und gibt ihnen seine Autoschlüssel:
#include <iostream>
using namespace std;
class CCarKeys
{
public:
CCarKeys(const string& Name) : _Name(Name) {}
string _Name;
};
class CChild
{
public:
CChild(CCarKeys& CarKeys) : _Name("Child"), _CarKeys(CarKeys) {}
string _Name;
CCarKeys& _CarKeys;
void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
};
class CDad
{
public:
CDad() : _Name("Dad"), _HondaCarKeys("Honda keys"), _ChevyCarKeys("Chevy keys") {}
string _Name;
CCarKeys _HondaCarKeys;
CCarKeys _ChevyCarKeys;
CChild *_Boy;
CChild *_Girl;
void MakeBoy() {_Boy= new CChild(_HondaCarKeys);}
void MakeGirl() {_Girl= new CChild(_ChevyCarKeys);}
};
int main ()
{
CDad Dad;
Dad.MakeBoy();
Dad.MakeGirl();
Dad._Boy->TestHasKeys();
Dad._Girl->TestHasKeys();
}
Lösung
Sieht mir gut (wenn alle Schlüssel ist, dass sie benötigen). Sie könnten einige andere Dienste von Dad müssen, die später angefordert werden - wie:
Wallet += MyDad.GasMoney(REQUEST_MAX_AND_PROMISE_TO_BE_HOME_BY_10PM) ;
Aber sie haben nicht einen Verweis auf Dad, so dass sie nicht in der Lage sein, das zu tun. So würde ich das CChild Konstruktor einen this
Bezug nehmen, auch.
class ICashProvider {
public:
virtual money Request(IPerson,CashRequestFlags) ;
};
class IChaffeur {
public:
virtual void Drive(IPerson[]) ;
};
etc.
Und dann CChild
Konstruktor müßte ICashProvider
und IChaffeur
nehmen, wie es CWife
und CGirlfriend
(und CBoyfriend
, vielleicht). An diesem Punkt, ich glaube, Sie könnten erkennen, dass diese Ebene der Granularität im Angesicht der Dad
Verantwortlichkeiten sinnlos ist und Sie nur jeder this
geben und haben Dad
Anfragen zu authentifizieren, indem Anrufer zwingen, ihre eigenen this
auf einige Methoden zu senden, so dass Sie nicht Dad
Durchführung Inzest oder die CWife
Windel haben zu ändern.
Andere Tipps
Passing durch Bezugnahme ist ziemlich das gleiche, die durch Zeiger vorbei mit Ausnahme Semantik und die Tatsache, dass Sie nichts mit dem Zeiger selbst wenn vorbeiReferenz tun können.
Der Code ist in Ordnung.
In Ihrem speziellen Fall, Autoschlüssel sind wahrscheinlich nicht dauerhaft gewährt werden, sondern bei Bedarf angefordert und gewährt auf je Anfrage. Es ist also mehr
class Dad
{
/** may return NULL if no keys granted */
CarKeys *requestKeys(CChild forChild);
}
In mehr genral Fall der Haupt-App-Klasse und Kinder, wie etwa das Datenobjekt von App und Kindern in Haupt geteilt zu schaffen () und thereferences jedem vorbei.
Es ist möglich, und in Ihrem Code verursacht es nicht schaden. Aber es ist gefährlich, denn wenn man CDAD kopieren, dann werden die Schlüssel und Zeiger entlang kopiert werden. Die Objekte, die die Zeiger-zu-Punkt, und die Referenz innerhalb dieser Objekte jedoch werden das gleiche bleiben. Wenn das Original dann CDAD Objekt den Gültigkeitsbereich verlässt, baumeln die Verweise in den Objekten durch die Zeiger verwiesen wird, mehr kein gültiges Objekt verweist.
Vielleicht können Sie Leben umkehren: Erstellen Sie die Tasten auf dem Heap, und die Kinder als normale Mitglieder innerhalb der Klasse. Also, wenn Sie Papa kopieren, werden die Kinder kopiert, aber die Tasten nicht. Ich denke, Schlüssel unveränderlich sind, so können Sie den gleichen Schlüssel auf mehrere Kinder teilen.
Das bringt zu einem anderen Punkt: Wenn Sie Ihre Schlüssel vernünftig klein sind (sprich: nicht sehr groß) und unveränderlich (so haben Sie keine Update-Anomalien, wenn Sie einen Schlüssel zu ändern, aber nicht die andere), hält es die Schaffung nicht auf dem Heap zu - so werden sie automatisch zusammen zu kopiert, und sie an die Kinder weitergeben, wenn sie den Schlüssel benötigen. Sie können sie normale Zeiger der Kinder machen, aber ich denke, das ist hässlich, weil das Kind nicht enthalten ein Schlüssel - aber nutzt. So ein Zeiger / Verweis oder ein Funktionsparameter paßt gut, aber nicht ein „echtes“ Datenelement.
Wenn Sie mit dem Shared-Key werden und die Schlüssel-on-Haufen, sollten Sie intelligente Zeiger verwenden - weil Sie den Überblick über alle Kinder und Väter zu halten haben. Wenn das letzte Kind / Vater den Gültigkeitsbereich verlässt, müssen Sie den Schlüssel wieder gelöscht werden. Sie verwenden boost::shared_ptr
dafür:
class CCarKeys
{
public:
CCarKeys(const string& Name) : _Name(Name) {}
string _Name;
};
class CChild
{
public:
CChild(boost::shared_ptr<CCarKeys> const& CarKeys)
: _Name("Child"), _CarKeys(CarKeys) {}
string _Name;
boost::shared_ptr<CCarKeys> _CarKeys;
void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
};
class CDad
{
public:
// NOTE: null the kid pointers *if* you use raw pointers, so you can check whether
// a boy or girl is present. Without nulling them explicitly, they have
// indeterminate values. Beware. shared_ptr's however will automatically
// initialized to default "null" values
CDad() :
_Name("Dad"),
_HondaCarKeys(new CCarKeys("Honda keys")),
_ChevyCarKeys(new CCarKeys("Chevy keys")) {}
string _Name;
boost::shared_ptr<CCarKeys> _HondaCarKeys;
boost::shared_ptr<CCarKeys> _ChevyCarKeys;
// also use shared_ptr for the kids. try to avoid raw pointers.
boost::shared_ptr<CChild> _Boy;
boost::shared_otr<CChild> _Girl;
void MakeBoy() {_Boy.reset(new CChild(_HondaCarKeys));}
void MakeGirl() {_Girl.reset(new CChild(_ChevyCarKeys));}
};
// main can be used unchanged
Natürlich können Sie all diese Komplexität vermeiden, indem sie nur machen die CDAD Klasse nicht kopierbar. Dann können Sie Ihre ursprüngliche Lösung verwenden, nur mit der Herstellung die Kinder eine shared_ptr nutzen und die Kinder nicht kopierbar machen. Idealerweise sollte man einen nicht-freigegebenen Zeiger, wie auto_ptr
verwenden, aber das auto_ptr hat einige Tücken, die Shared_ptr alle vermeidet:
class CCarKeys
{
public:
CCarKeys(const string& Name) : _Name(Name) {}
string _Name;
};
class CChild
{
public:
CChild (CCarKeys& CarKeys)
: _Name("Child"), _CarKeys(CarKeys) {}
string _Name;
CCarKeys &_CarKeys;
void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
private:
CChild(CChild const&); // non-copyable
CChild & operator=(CChild const&); // non-assignable
};
class CDad
{
public:
CDad() :
_Name("Dad"),
_HondaCarKeys("Honda keys"),
_ChevyCarKeys("Chevy keys") {}
string _Name;
CCarKeys _HondaCarKeys;
CCarKeys _ChevyCarKeys;
// also use shared_ptr for the kids. try to avoid raw pointers.
boost::shared_ptr<CChild> _Boy;
boost::shared_otr<CChild> _Girl;
void MakeBoy() {_Boy.reset(new CChild(_HondaCarKeys));}
void MakeGirl() {_Girl.reset(new CChild(_ChevyCarKeys));}
private:
CDad(CDad const&); // non-copyable
CDad & operator=(CDad const&); // non-assignable
};
Wenn ich eine solche Klassenhierarchie zu implementieren wäre, hätte ich mit dieser Lösung gehen, oder einfach nur die Schlüssel als Mitglieder fallen, und übergeben / sie schaffen, wenn die Kinder benötigt. Einige andere Hinweise über Ihren Code:
- Bessere fällt den „_“ von Mitgliedern oder sie am Ende setzen oder eine andere Schreibweise verwenden. Ein Name, der mit einem Unterstrich beginnt und wird von einem Großbuchstaben gefolgt ist reserviert von C ++ Implementierungen (Compiler, C ++ std lib ...).
- Ich persönlich finde es verwirrend Mitgliedernamen haben und Variablen mit einem Großbuchstaben beginnen. Ich habe es nur sehr selten gesehen. Aber das ist nicht viel zu kümmern, es ist nur über persönlichen Stil.
- Es gibt eine berühmte Regel ( Null-Eins-Infinity-), die besagt, wenn Sie bekam zwei Dinge von etwas, sollten Sie generell in der Lage sein, beliebig viele Dinge davon zu haben. Also, wenn Sie zwei Kinder haben - warum haben nicht viele von ihnen? Zwei scheint wie eine willkürliche Wahl. Aber es kann einen guten Grund, in Ihrem Fall hat -. So dass diese ignorieren, wenn in Ihrem Fall ist es sinnvoll