Читаем Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ полностью

Это делает реализацию оператора new для Widget совсем простой:


void Widget::orerator new(std::size_td size) throw(std::bad_aloc)

{

NewHandlerHolder // установить обработчик

h(std::set_new_handler(currentHandler)); // new из класса Widget

return ::operator new(size); // выделить память или

// возбудить исключение

} // восстановить глобальный

// обработчик new


Пользователи класса Widget применяют эти средства следующим образом:


void outOfMem; // объявление функции, которую нужно

// вызвать, если выделить память

// для Widget не удается

Widget::set_new_handler(outOfmem); // установка outOfMem в качестве

// обработчика new для Widget

Widget *pw1 = new Widget; // если выделить память не удалось,

// вызывается outOfMem

std::string *ps = new std::string; // если выделить память не удалось,

// вызывается глобальный обработчик new

// (если есть)

Widget::set_new_handler(0); // отменяет обработчик new

Widget *pw1 = new Widget; // если выделить память не удалось,

// сразу же возбуждается исключение (никакого

// обработчика new сейчас нет)


Код, реализующий эту схему, один и тот же (независимо от класса), поэтому разумно было бы повторно использовать его в других местах. Простой способ сделать это – создать «присоединяемый» базовый класс, то есть базовый класс, который предназначен для того, чтобы подклассы могли унаследовать одно-единственное средство, в данном случае способность устанавливать специфичный для класса обработчик new. Затем превратите базовый класс в шаблон, чтобы каждый производный класс мог получать разные копии данных.

При таком подходе принадлежащая базовому классу часть позволяет подклассам наследовать необходимые им функции set_new_handler и operator new, а шаблонная часть гарантирует, что у каждого подкласса будет собственный член данных currentHandler. Звучит сложновато, но код выглядит обнадеживающе знакомым. Фактически единственным отличием является то, что теперь он доступен любому классу:


templatetypename T // «присоединяемый» базовый класс для

class NewHandlerSupport { // поддержки специфичной для класса

public: // функции set_new_handler

static std::new_handler set_new_handler(std::new_handler p) throw;

static void *operator new(std::size_t size) throw(std::bad_alloc);

... // другие версии оператора new – см. правило 52

private:

static std::new_handler currentHandler;

};

templatetypename T

std::new_handler

NewHandlerSupportT::set_new_handler(std::new_handler p) throw

{

std::new_handler oldHandler = currentHandler;

currentHandler = p;

return oldHandler;

}

templatetypename T

void *NewHandlerSupportT::operator(std::size_t size)

throw(std::bad_alloc)

{

NewHandlerHolder h(std::set_new_handler(currentHandler);

return ::operator new(size);

}

// currentHandler в любом классе инициализируется значением null

templatetypename T

std::new_handler NewHandlerSupportT::currentHandler = 0;


С этим шаблоном класса добавление поддержки set_new_handler к Widget очень просто: Widget просто наследуется от NewHandlerSupportWidget. (Это может показаться экстравагантным, но ниже я подробно объясню, что здесь происходит.)


class Widget: public NewHandlerSupportWidget {

... // как раньше, но без декларации

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже