void* operator new(std::size_t); // Обычный
void* operator new(std::size_t,
std::nothrow_t) throw(); // He генерирующий исключений
void* operator new(std::size_t,
void*); // Размещающий
В качестве другого варианта событий предположим, что ваш класс предоставляет некоторую специфичную для данного класса версию оператора new
— одну из трех. В таком случае это объявление также скроет остальные две версии:
class С {
// ...
// Скрывает две другие стандартные версии оператора new
static void* operator new(size_t, void*);
};
Предпочтительно, чтобы у класса С в его область видимости были явно внесены все три стандартные версии оператора new
. Обычно все они должны иметь одну и ту же видимость. (Видимость для отдельных версий может быть сделана закрытой, если вы хотите явно запретить один из вариантов оператора new
, однако цель данной рекомендации — напомнить, чтобы вы не скрыли эти версии непреднамеренно.)
Заметим, что вы должны всегда избегать сокрытия размещающего new
, поскольку он интенсивно используется контейнерами стандартной библиотеки.
Все, что осталось упомянуть, — это то, что внесение оператора new
в область видимости может быть сделано двумя различными способами в двух разных ситуациях. Если базовый класс вашего класса также определяет оператор new
, все, что вам надо, — "раскрыть" оператор new
:
class С : public B { // ...
public:
using B::operator new;
};
В противном случае, если не имеется базового класса или в нем не определен оператор new
, вы должны написать короткую пересылающую функцию (поскольку нельзя использовать using для внесения имен из глобальной области видимости):
class C { // ...
public:
static void* operator new(std::size_t s) {
return ::operator new(s);
}
static void* operator new(std::size_t s,
std::nothrow_t nt) throw() {
return ::operator new(s, nt);
}
static void* operator new(std::size_t s, void* p) {
return ::operator new(s, p);
}
};
Рассмотренная рекомендация применима также к версиям операторов для массивов — operator new[]
.
Избегайте вызова версии new(nothrow)
в вашем коде, но тем не менее обеспечьте и ее, чтобы пользователи вашего класса не оказались в какой-то момент неприятно удивлены.
Ссылки
[Dewhurst03] §60 • [Sutter04] §22-23
Конструкторы, деструкторы и копирование
Если стандарт привел вас к обрыву, это еще не значит, что вы должны прыгнуть с него.
— Норман Даймонд (Norman Diamond)
О Большой Четверке специальных функций было сказано достаточно, чтобы вы не удивлялись тому, что им посвящен отдельный раздел. Здесь собраны знания и практика, связанные с конструкторами по умолчанию, копирующими конструкторами, копирующим присваиванием и деструкторами.
Одна из причин, по которым при работе с этими функциями следует быть особенно внимательными, заключается в том, что если вы дадите компилятору хотя бы полшанса — он тут же напишет эти функции за вас. Еще одна причина состоит в том, что С++ по умолчанию рассматривает классы как типы-значения, но далеко не все типы именно таковы (см. рекомендацию 32). Надо отчетливо понимать, когда следует писать (или запрещать) эти специальные функции явно, и следовать правилам и рекомендациям из этого раздела — это поможет вам в написании корректного, расширяемого и безопасного кода.
В этом разделе мы считаем наиболее значимой рекомендацию 51 — "Деструкторы, функции освобождения ресурсов и обмена не ошибаются".
47. Определяйте и инициализируйте переменные-члены в одном порядке
Резюме
Переменные-члены всегда инициализируются в том порядке, в котором они объявлены при определении класса; порядок их упоминания в списке инициализации конструктора игнорируется. Убедитесь, что в коде конструктора указан тот же порядок, что и в определении класса.
Обсуждение
Рассмотрим следующий код:
class Employee {
string email_, firstName_, lastName_;
public:
Employee( const char* firstName, const char* lastName ) :
firstName_(firstName), lastName_(lastName),
email_(firstName_+"."+lastName_+"@acme.com") {}
};
Этот код содержит ошибку, столь же неприятную, сколь и трудно обнаруживаемую. Поскольку член email_
объявлен в определении класса до first_
и last_
, он будет инициализирован первым и будет пытаться использовать еще не инициализированные поля. Более того, если определение конструктора находится в отдельном файле, то выявить такое удаленное влияние порядка объявления переменных-членов класса на корректность конструктора окажется еще труднее.
Читать дальше