Материалы книги получены с http://www.itlibitum.ru/
Конструирование
Предположим, вы разрешили пользователям создавать собственные указываемые объекты и передавать их ведущим указателям во время конструирования.
template <class Type>
class MP {
private:
Type* t;
public:
MP() : t(NULL) {}
MP(Type* pointer) : t(pointer) {}
~MP() { delete t; }
// И т.д.
};
Foo* foo = new Foo;
MP<Foo> mp1(foo);
MP<Foo> mp2(foo); // Облом!
Насколько проще была бы жизнь без таких пользователей! Когда mp1 удаляется, пропадает и foo. В итоге mp2 начинает указывать неизвестно куда. «Зачем кому-нибудь потребуется вытворять такое?» - спросите вы. Как говорилось в одном рекламном ролике: «Зачем спрашивать «Зачем»?» Если вы оставите такую дыру, можете не сомневаться: кто-нибудь когда-нибудь изобретет дьявольский план, использует ее и обвинит во всех смертных грехах вашу программу.
Пользователь прямо-таки кричит: «Держите меня, пока я не натворил бед». Для этого существует надежный способ: отобрать у него ключи от конструкторов класса указываемого объекта.
class Foo {
friend class MP<Foo>;
protected:
Foo(); // Теперь доступ к конструктору имеет только MP<Foo>
public:
// Оставшаяся часть интерфейса
};
template <class Type>
class MP {
private:
Type* t;
public:
MP() : t(new Type) {}
// И т.д.
};
Aгa, уже лучше. При создании указателя его конструктор также конструирует и указываемый объект. Объявляя указатель другом, мы можем сделать конструкторы Foo закрытыми или защищенными и сохранить доступ к ним из конструкторов указателя. Теперь клиент никак не сможет добраться до Foo, кроме как через MP<Foo>. Мы еще неоднократно вернемся к вопросу о том, как, когда и где создавать указываемые объекты, а пока давайте немного отвлечемся. Если конструкторы Foo вызываются с аргументами, существуют две альтернативы:
1. Вместо того чтобы пользоваться универсальным шаблоном ведущего указателя, создайте для
Foo нестандартный класс ведущего указателя MPFoo. Для каждого конструктора Foo создайте
конструктор MPFoo с точно такой же сигнатурой и передайте его аргументы конструктору Foo.
2. Воспользуйтесь безаргументным конструктором для создания объекта и предоставьте
отдельную функцию инициализации, которая может вызываться клиентом после
конструирования.
Второй вариант выглядит так:
class Foo {
friend class MP<Foo>;
protected:
Foo(); // Единственный конструктор
public:
Initialized(int, char*);
// Оставшаяся часть интерфейса
};
MP<Foo> mpf;
mpf->Initialize(17, "Hello"); // Завершить конструирование
Такое решение выглядит довольно неуклюже, но оно позволяет работать с универсальным шаблоном ведущего указателя. Существуют и другие причины для использования инициализирующих функций, о которых будет рассказано в следующих главах. Любой из этих вариантов вполне приемлем для решения наших текущих задач.
Назад Содержание Далее
|