Следующая запись в 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.
Пример 8.2. Функции, создающие объекты
#include
class Session {};
class SessionFactory {
public:
Session Create();
Session* CreatePtr();
void Create(Session*& p);
// ...
};
// Возвращаем копию объекта в стеке
Session SessionFactory::Create() {
Session s;
return(s);
}
// Возвращаем указатель на объект в куче
Session* SessionFactory::CreatePtr() {
return(new Session());
}
// Обновляем переданный указатель, записывая адрес
// нового объекта
void SessionFactory::Create(Session*& p) {
p = new Session();
}
static SessionFactory f; // Единственный объект фабрики
int main() {
Session* p1;
Session* p2 = new Session();
*p2 = f.Create(); // Просто присваиваем объект, полученный из Create
p1 = f.CreatePtr(); // или полученный указатель на объект в куче
f.Create(p1); // или обновляем указатель новым адресом
}
Обсуждение
Пример 8.2 показывает несколько различных способов написания функции, возвращающей объект. Сделать так вместо обращения к newможет потребоваться, если создаваемый объект берется из пула, связан с оборудованием или удаление объектов должно управляться не вызывающим кодом. Существует множество причин использовать этот подход (и именно поэтому существует шаблон проектирования для него), я привел только некоторые. К счастью, реализация шаблона фабрики в C++ очень проста.
Наиболее часто используют возврат адреса нового объекта в куче или обновление адреса указателя, переданного как аргумент. Их реализация показана в примере 8.2, и она тривиальна и не требует дальнейших пояснений. Однако возврат из функции целого объекта используется реже — возможно, потому, что это требует больших накладных расходов.
Читать дальше