Это возможно, и в вашем коде это не приносит вреда. Но это опасно, потому что если вы копируете CDad, то ключи и указатели будут скопированы вместе. Объекты, на которые будут указывать указатели, и ссылка внутри этих объектов, тем не менее, останутся прежними. Если исходный объект CDad выходит из области видимости, ссылки в объектах, на которые ссылаются указатели, висят и больше не ссылаются на действительный объект.
Может быть, вы можете изменить время жизни: создайте ключи в куче, и дети как обычные члены в классе. Поэтому, если вы копируете папу, дети копируются, а ключи - нет. Я думаю, что ключи являются неизменяемыми, поэтому вы можете использовать один и тот же ключ среди нескольких детей.
Это приводит к другому вопросу: если ваши ключи достаточно малы (читай: не огромны) и неизменны (поэтому у вас нет аномалий обновления, если вы меняете один ключ, но не другие), рассмотрите возможность его создания не в куче тоже - так что они тоже автоматически копируются и передают их детям, когда им нужен ключ. Вы можете сделать их обычными указателями на детей, но я думаю, что это ужасно, потому что ребенок не содержит ключ *, а использует его. Таким образом, указатель / ссылка или параметр функции хорошо подходит, но не «реальный» элемент данных.
Если вы идете с общим ключом и ключами на куче, вы должны использовать умные указатели - потому что вы должны отслеживать всех детей и пап. Если последний ребенок / папа выходит из области видимости, вам придется снова удалить ключ. Для этого вы используете 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 ...).
- Лично меня смущает, что имена членов и переменные начинаются с заглавной буквы. Я видел это очень редко. Но это не очень важно, это просто личный стиль.
- Существует известное правило ( Zero-One-Infinity ), которое гласит, что когда вы получаете две вещи чего-то, вы, как правило, должны иметь возможность иметь множество таких вещей. Итак, если вы можете иметь двоих детей - почему бы не иметь их много? Два кажется произвольным выбором. Но это может иметь вескую причину в вашем случае - поэтому игнорируйте это, когда в вашем случае это имеет смысл.