Герб Саттер - Стандарты программирования на С++. 101 правило и рекомендация
- Название:Стандарты программирования на С++. 101 правило и рекомендация
- Автор:
- Жанр:
- Издательство:Издательский дом Вильямс
- Год:2005
- Город:Москва
- ISBN:5-8459-0859-0
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Герб Саттер - Стандарты программирования на С++. 101 правило и рекомендация краткое содержание
Эта книга поможет новичку стать профессионалом, так как в ней представлен сконцентрированный лучший опыт программистов на С++, обобщенный двумя экспертами мирового класса.
Начинающий программист найдет в ней простые и понятные рекомендации для ежедневного использования, подкрепленные примерами их конкретного применения на практике.
Опытные программисты найдут в ней советы и новые рекомендации, которые можно сразу же принять на вооружение. Программисты-профессионалы могут использовать эту книгу как основу для разработки собственных стандартов кодирования, как для себя лично, так и для группы, которой они руководят.
Конечно, книга рассчитана в первую очередь на профессиональных программистов с глубокими знаниями языка, однако она будет полезна любому, кто захочет углубить свои знания в данной области.
Стандарты программирования на С++. 101 правило и рекомендация - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Размещающий оператор new
void* T::operator new(size_t, void* p) { return p; }
не требует наличия соответствующего оператора delete
, поскольку реального выделения памяти при этом не происходит. Все протестированные нами компиляторы не выдавали никаких предупреждений по поводу отсутствия оператора void T::operator delete(void*, size_t, void*)
.
[C++03] §5.3.4 • [Stroustrup00] §6.2.6.2, §15.6 • [Sutter00] §36
46. При наличии пользовательского new
следует предоставлять все стандартные типы этого оператора
Если класс определяет любую перегрузку оператора new
, он должен перегрузить все три стандартных типа этого оператора — обычный new
, размещающий и не генерирующий исключений. Если этого не сделать, то эти операторы окажутся скрытыми и недоступными пользователям вашего класса.
Обычно пользовательские операторы new
и delete
нужны очень редко, но если они все же оказываются необходимы, то вряд ли вы захотите, чтобы они скрывали встроенные сигнатуры.
В С++, после того как вы определите имя в области видимости (например, в области видимости класса), все такие же имена в охватывающих областях видимости окажутся скрыты (например, в базовых классах или охватывающих пространствах имен), так что перегрузка никогда не работает через границы областей видимости. Когда речь идет об имени оператора new
, необходимо быть особенно осторожным и внимательным, чтобы не усложнять жизнь себе и пользователям вашего класса.
Пусть вы определили следующий оператор new
, специфичный для класса:
class С {
// ...
// Скрывает три стандартных вида оператора new
static void* operator new(size_t, MemoryPool&);
};
Теперь, если кто-то попытается написать выражение с обычным стандартным new
С, компилятор сообщит о том, что он не в состоянии найти обычный старый оператор new
. Объявление перегрузки C::operator new
с параметром типа MemoryPool
скрывает все остальные перегрузки, включая знакомые встроенные глобальные версии, которые все мы знаем и любим:
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_
, он будет инициализирован первым и будет пытаться использовать еще не инициализированные поля. Более того, если определение конструктора находится в отдельном файле, то выявить такое удаленное влияние порядка объявления переменных-членов класса на корректность конструктора окажется еще труднее.
Интервал:
Закладка: