static void operator delete(void *pMemory, void *ptr) throw()
{ ::operator delete(pMemory, ptr);}
// не возбуждающие исключений new/delete
static void *operator new(std::size_t, const std::nothrow_t& nt) throw()
{ return ::operator new(size, nt)}
static void operator delete(void *pMemory, const std::nothrow_t&) throw()
{ ::operator delete(pMemory, nt);}
};
Пользователи, которые хотят пополнить свой арсенал специальными формами new, применяют наследование и using-объявления (см. правило 33), чтобы получить доступ к стандартным формам:
class Widget: public StandardNewDeleteForms { // наследование
public: // стандартных форм
using StandardNewDeleteForms::operator new; // сделать эти формы
using StandardNewDeleteForms::operator delete; // видимыми
static void *operator new(std::size_t size, // добавляется
std::ostream& logStream) // специальный
throw(bad_alloc); // размещающий new
static void operator delete(void *pMemory, // добавляется
std::ostream& logStream) // соответствующий
throw(); // размещающий delete
...
};
• Когда вы пишете размещающую версию оператора new, убедитесь, что не забыли о соответственном размещающем операторе delete. Если его не будет, то в вашей программе могут возникать тонкие, трудноуловимые утечки памяти.
• Объявляя размещающие версии new и delete, позаботьтесь о том, чтобы нечаянно не скрыть нормальных версий этих функций.
Глава 9
Разное
Несмотря на то что эта глава состоит всего из трех правил, все они очень важны.
В первом правиле подчеркивается, что предупреждения компилятора – не пустяк, на который можно не обращать внимания. По крайней мере, если вы хотите, чтобы ваши программы вели себя правильно. Во втором представлен обзор стандартной библиотеки C++, включая и новую функциональность, предложенную в отчете TR1. И наконец, в последнем правиле представлен обзор проекта Boost – возможно, наиболее важного Web-сайта, посвященного общим вопросам применения C++. Игнорируя советы, изложенные в этих правилах, писать эффективные программы на C++ как минимум нелегко.
Правило 53: Обращайте внимание на предупреждения компилятора
Многие программисты зачастую игнорируют предупреждения компилятора. В конце концов, если бы проблема была по-настоящему серьезной, компилятор выдал бы ошибку! Подобные рассуждения могут быть сравнительно безвредными при работе с какими-нибудь другими языками, но в отношении C++ можно поручиться, что создатели компиляторов точнее вас оценивают истинное положение дел. Например, ниже приведена ошибка, которую рано или поздно допускает каждый из нас:
class B {
public:
virtual void f() const;
};
class D: public B {
public:
virtual void f();
};
Предполагается, что функция D::f будет переопределять виртуальную функцию B::f, но ошибка состоит в следующем: в классе B функция-член f – константная, а в D она не объявляется как const. Один из известных мне компиляторов сообщает следующее:
warning: D::f() hides virtual B::f()
(предупреждение: D::f() скрывает virtual B::f())
Многие неопытные программисты, получив подобное сообщение, говорят себе: «Конечно, D::f скрывает B::f – так и должно быть!» Они неправы. Вот что пытается сказать компилятор: f, объявленная в B, не была объявлена повторно в D, а полностью спрятана (объяснение причины этого явления см. в правиле 33). Если оставить без внимания данное предупреждение, это почти наверняка приведет к ошибочному поведению программы, и, чтобы найти причину, потребуются долгие часы отладки – при том, что компилятор давно уже все обнаружил.