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
Наиболее часто используют возврат адреса нового объекта в куче или обновление адреса указателя, переданного как аргумент. Их реализация показана в примере 8.2, и она тривиальна и не требует дальнейших пояснений. Однако возврат из функции целого объекта используется реже — возможно, потому, что это требует больших накладных расходов.
При возврате временного объекта в стеке тела функции создается временный объект. При выходе из функции компилятор копирует данные из временного объекта в другой временный объект, который и возвращается из функции, Наконец, в вызывающей функции объекту с помощью оператора присвоения присваивается значение временного объекта. Это означает, что на самом деле создается два объекта: объект в функции фабрики и временный объект, который возвращается из функции, содержимое которого затем копируется в целевой объект. Здесь осуществляется большое количество копирований (хотя компилятор может оптимизировать временный объект), так что при работе с большими объектами или частыми вызовами этой функции фабрики внимательно следите за тем, что в ней происходит.
Также эта методика копирования временных объектов работает только для объектов, которые ведут себя как
Если вы пишете функцию или метод, создающий объекты, то посмотрите также рецепт 8.12. Используя шаблоны, функций можно написать одну функцию, которая будет возвращать новый объект любого типа. Например:
template
T* createObject() {
return(new T());
}
MyClass* p1 = createObject();
MyOtherClass* p2 = createObject();
// ...
Этот подход удобен, если требуется единственная функция фабрики, которая сможет одинаковым образом создавать объекты любых классов (или группы связанных классов), что позволит избежать избыточного многократного кодирования функции фабрики.
Рецепт 8.12.
8.3. Использование конструкторов и деструкторов для управления ресурсами (RAII)
Для класса, представляющего некоторый ресурс, требуется использовать конструктор для получения этого ресурса и деструктор для его освобождения. Эта методика часто называется «получение ресурсов как инициализация» (
Выделите или получите ресурс в конструкторе и освободите этот ресурс в деструкторе. Это снизит объем кода, который пользователь класса должен будет написать для обработки исключений. Простая иллюстрация этой методики показана в примере 8.3.
#include
#include
using namespace std;
class Socket {
public:
Socket(const string& hostname) {}
};
class HttpRequest {
public:
HttpRequest(const string& hostname) :
sock_(new Socket(hostname)) {}