Главная страница Библиотека (скачать книги) Скачать софт Введение в программирование Стандарты для C++ Уроки по C# Уроки по Python HTML Веб-дизайн Ассемблер в среде Windows ActiveX Javascript Общее о Линукс Линукс - подробно Линукс - новое Delphi Паскаль для начинающих Турбопаскаль Новости Партнеры Наши предложения Архив новостей |
46. При наличии пользовательского new следует предоставлять все стандартные типы этого оператораРезюмеЕсли класс определяет любую перегрузку оператора new, он должен перегрузить все три стандартных типа этого оператора — обычный new, размещающий и не генерирующий исключений. Если этого не сделать, то эти операторы окажутся скрытыми и недоступными пользователям вашего класса. ОбсуждениеОбычно пользовательские операторы new и delete нужны очень редко, но если они все же оказываются необходимы, то вряд ли вы захотите, чтобы они скрывали встроенные сигнатуры. В C++, после того как вы определите имя в области видимости (например, в области видимости класса), все такие же имена в охватывающих областях видимости окажутся скрыты (например, в базовых классах или охватывающих пространствах имен), так что перегрузка никогда не работает через границы областей видимости. Когда речь идет об имени оператора new, необходимо быть особенно осторожным и внимательным, чтобы не усложнять жизнь себе и пользователям вашего класса. Пусть вы определили следующий оператор new, специфичный для класса: class С { // ... // Скрывает три стандартных вида оператора new static void* operator new(size_t, MemoryPool&); }; Теперь, если кто-то попытается написать выражение с обычным стандартным new C, компилятор сообщит о том, что он не в состоянии найти обычный старый оператор new. Объявление перегрузки C::operator new с параметром типа MemoryPool скрывает все остальные перегрузки, включая знакомые встроенные глобальные версии, которые все мы знаем и любим: void* operator new(std::size_t); // Обычный void* operator new(std::size_t, std::nothrow_t) throw(); // Не генерирующий исключений 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 С {// ... 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) в вашем коде, но тем не менее обеспечьте и ее, чтобы пользователи вашего класса не оказались в какой-то момент неприятно удивлены. |
|
Библиотека программиста. 2009. |
|