それは可能であり、あなたのコードでは害はありません。ただし、CDad をコピーすると、キーとポインターも一緒にコピーされるため、危険です。ただし、ポインターが指すオブジェクトと、それらのオブジェクト内の参照は同じままです。その後、元の CDad オブジェクトが範囲外になると、ポインターによって参照されるオブジェクト内の参照がぶら下がり、有効なオブジェクトを参照しなくなります。
おそらく、ライフタイムを逆にすることができます。ヒープにキーを作成し、クラス内の通常のメンバーとして子供を作成します。つまり、お父さんをコピーすると、子供はコピーされますが、鍵はコピーされません。鍵は不変だと思うので、複数の子供で同じ鍵を共有できます。
これは別のポイントをもたらします:あなたのキーが適度に小さく(読む:巨大ではない)、不変である(したがって、1つのキーを変更しても他のキーを変更しない場合に更新異常がない場合)、ヒープ上に作成しないことも検討してください-そのため、それらも自動的にコピーされ、鍵が必要なときに子供たちに渡されます。それらを子供たちの通常のポインタにすることはできますが、子供にはキーが含まれていませんが、それを使用しているため、それは醜いと思います。したがって、ポインター/参照または関数パラメーターは適切に適合しますが、「実際の」データ メンバーには適合しません。
共有キーとキーオンヒープを使用する場合は、すべての子供と父親を追跡する必要があるため、スマート ポインターを使用する必要があります。最後の子供/お父さんが範囲外になった場合は、キーを再度削除する必要があります。そのために使用しますboost::shared_ptr
:
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
もちろん、CDad クラスをコピー不可にするだけで、この複雑さをすべて回避できます。次に、子供たちにshared_ptrを使用させ、子供たちもコピー不可にするだけで、元のソリューションを使用できます。理想的には、 などの非共有ポインタを使用する必要がありますauto_ptr
が、auto_ptr にもいくつかの落とし穴があり、shared_ptr はすべて回避します。
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
};
そのようなクラス階層を実装する必要がある場合は、そのソリューションを使用するか、キーをメンバーとしてドロップし、必要に応じて子に渡す/作成します。コードに関するその他の注意事項:
- メンバーから「_」を削除するか、最後に配置するか、他の表記を使用することをお勧めします。アンダースコアで始まり、その後に大文字が続く名前は、C++ 実装 (コンパイラ、C++ std lib ...) によって予約されています。
- 個人的には、メンバー名と変数が大文字で始まるのはややこしいと思います。ごく稀にしか見たことがありません。しかし、これはあまり気にする必要はありません。個人的なスタイルの問題です。
- 有名なルール (ゼロワンインフィニティ) があります。あるものを 2 つ取得した場合、通常、任意の数のものを取得できるはずです。では、2 人の子供を持てるのであれば、なぜ多くの子供を持たないのでしょうか? 2 は恣意的な選択のように思えます。しかし、あなたのケースでは正当な理由があるかもしれません - あなたのケースでそれが理にかなっているときはこれを無視してください.