В конструкторе по умолчанию Foo
cls_
не требуется, так как будет вызван ее конструктор по умолчанию. Но если требуется создать Foo
с аргументами, то следует добавить аргумент в список инициализации, как это сделано выше, а не делать присвоение в теле конструктора. Используя список инициализации, вы избежите дополнительного шага создания cls_
(так как при присвоении cls_
значения в теле конструктора cls_
вначале создается с использованием конструктора по умолчанию, а затем с помощью оператора присвоения выполняется присвоение нового значения), а также получите автоматическую обработку исключений. Если объект создается в списке инициализации и этот объект в процессе его создания выбрасывает исключение, то среда выполнения удаляет все ранее созданные объекты списка и передает исключение в код, вызывавший конструктор. С другой стороны, при присвоении аргумента в теле конструктора такое исключение необходимо обрабатывать с помощью блока try/catch
.Ссылки более сложны: инициализация переменной-ссылки (и const
Следующая запись в C++ недопустима.
int& x;
Это значит, что невозможно объявить переменную-ссылку без ее инициализации. Вместо этого ее требуется инициализировать каким-либо объектом. Для переменных, не являющихся членами класса, инициализация может выглядеть вот так.
int а;
int& x = a;
Это все замечательно, но приводит к возникновению проблемы при создании классов. Предположим, вам требуется переменная-член класса, являющаяся ссылкой, как здесь.
class HasARef {
public:
int& ref;
};
Большинство компиляторов примет эту запись, но только до тех пор, пока вы не попытаетесь создать экземпляр этого класса, как здесь.
HasARef me;
В этот момент вы получите ошибку. Вот какую ошибку выдаст gcc.
error: structure 'me' with uninitialized reference members
(ошибка: структура 'me' с неинициализированными членами-ссылками)
Вместо этого следует использовать список инициализации.
class HasARef {
public:
int &ref
HasARef(int &aref) : ref(aref) {}
};
Затем при создании экземпляра класса требуется указать переменную, на которую будет ссылаться переменная ref
int var;
HasARef me(var);
Именно так следует безопасно и эффективно инициализировать переменные-члены. В общем случае всегда, когда это возможно, используйте список инициализации и избегайте инициализации переменных-членов в теле конструктора. Даже если требуется выполнить какие-либо действия с переменными в теле конструктора, список инициализации можно использовать для установки начальных значений, а затем обновить их в теле конструктора.
Рецепт 9.2.
8.2. Использование функции для создания объектов (шаблон фабрики)
Вместо создания объекта в куче с помощью new вам требуется функция (член или самостоятельная), выполняющая создание объекта, тип которого определяется динамически. Такое поведение достигается с помощью шаблона проектирования Abstract Factory (абстрактная фабрика).
Здесь есть две возможности. Вы можете:
• создать функцию, которая создает экземпляр объекта в куче и возвращает указатель на этот объект (или обновляет переданный в нее указатель, записывая в него адрес нового объекта);
• создать функцию, которая создает и возвращает временный объект.
Пример 8.2 показывает оба этих способа. Класс Session
new
), а их создание должно управляться каким-либо другим классом. В этом примере управляющий класс — это SessionFactory
.#include
class Session {};
class SessionFactory {
public:
Session Create();
Session* CreatePtr();
void Create(Session*& p);
// ...
};
// Возвращаем копию объекта в стеке
Session SessionFactory::Create() {
Session s;
return(s);
}
// Возвращаем указатель на объект в куче
Session* SessionFactory::CreatePtr() {