|
Программирование >> Обобщенные обратные вызовы
Размещающий, обычный и не генерирующий исключений оператор new 1. Какие три варианта оператора new описаны в стандарте С++? В стандарте С++ описаны три варианта оператора new и разрешено произвольное количество дополнительных перегрузок. Одним полезным видом этого оператора является размещающий new, который строит объект в имеющейся области памяти, без выделения нового пространства. Например: пример 22-1(а): использование размещающего new выделение достаточного количества памяти void* р = ::operator new( sizeof(т) ); new(p)T; создание объекта т по адресу р, вероятно, вызовом ::operator new(std::size t, void*) throwQ Стандарт также предоставляет обычный старый new , который не получает никаких дополнительных параметров и не генерирует исключений (nothrow new). Ниже представлен полный список перегруженных операторов new из стандарта С+ + . перегрузки оператора new в стандарте языка (имеются соответствующие версии и для new[]): void* ::operator new(std::size .t size) throw(std::bad alloc); обычный старый оператор new Использование: new т void* ::operator new(std::size t size, const std::nothrow t&) throwC); Оператор new, не генерирующий исключений использование: new (std::nothrow) т void* ::operator new(std::size t size, void* Размещающий оператор new использование: new (ptr) T ptr) throwO; Программа может заменить все, кроме последнего, операторы new на свои собственные версии. Все эти стандартные функции находятся в глобальной области действия, а не в пространстве имен std. Табл. 22.1 дает краткие характеристики стандартных версий new. Таблица 22.1. Сравнение стандартных версий оператора new
После выполнения оператора new вызывается конструктор объекта, и, конечно, этот конструктор может вызвать сбой - но в данном случае нас это не волнует. Сейчас нас интересует только вопрос о сбое в самом операторе new. Рассмотрим пример, показывающий применение описанных версий new. пример 22-1(6): использование перегрузок оператора new FastMemory f; new (f) T; Вызов некоторого пользовательского оператора new(stcl::si2e t, FastMemory&) (или аналогичного, с преобразованием типов), по-видимому, для выбора пользовательской области памяти new (42, 3.14159, xyzzy ) т; Вызов некоторого пользовательского оператора new(std: :size .t, int, double, const char*) (или аналогичного, с преобразованием типов) new (std:rnothrow) т; вероятно, вызов стандартного или пользовательского оператора ::new(std::size t, const std::nothrow t&) throwO В каждом из случаев в примерах 22-1а и 22-15 параметры в скобках в выражении с оператором new превращаются в дополнительные параметры, передаваемые оператору new при вызове. Конечно, в отличие от случая в примере 22-1а, все выражения в примере 22-1 б, вероятно, используют тот или иной механизм выделения памяти вместо ра:змещеиия объекта в некоторой заданной области памяти. Оператор new, специфичный для класса 2. Что такое оператор new, специфичный для класса, и как им следует пользоваться? Опишите, в каких случаях вы должны быть особо осторожны при предоставлении собственных, специфичных для класса операторов new и delete. Пом и.МО разрешения программе переопределить искогорые из глобальных операторов new, С++ также позволяет классу определить собственные версии операторов, специфичные для данного класса. При рассмотрении примеров 22-1а и 22-16 вы, наверное, обратили внимание на слово вероятно в двух комментариях? new(p)T; создание объекта т по адресу р, вероятно, вызовом ::operator new(std::size t, void*) throwO new (std::nothrow) T; Вероятно , вызов стандартного или пользовательского оператора : :new(std: :size .t, const std: : nothrow t&) throwO Почему только вероятно ? Потому что вызываемые операторы - не обязательно операторы из глобальной области видимости, а могут быть и специфичными для данного класса т. Для ясного понимания этого момента обратите внимание на два интересных взаимодействия между оператором new класса и глобальным оператором new. Хотя вы не можете непосредственно заменить стандартный размещающий оператор new, вы можете написать собственный размещающий оператор new для данного класса, который будет использован для размещения объектов этого класса, Вы можете добавить специфичный для класса оператор new, не генерирующий исключений, как с заменой соотаетствующего глобального оператора, так и без таковой. Таким образом, в приведенных строках кода возможна ситуация, когда т (или один из его базовых классов) предоставляет свои собственные версии одного (или обоих) из этих операторов, и в таком случае будут использованы именно эти операторы. Рассмотрим простой пример, в котором класс предоставляет все три варианта оператора new. пример 22-2: специфичные для класса версии new class X { public: static void* operator new(std: :size t ) throwO; 1 static void* operator new(std::size t, const std::nothrow t& ) throwC); 2 statiс void* operator new(std::size t, void* ) throwO ; 3 X* pi = new x; вызов 1 X* p2 = new (std::nothrow) X; вызов 2 void* p3 = /* некоторая область памяти, достаточная для размещения х */ ; ; new (рЗ) х; вызов 3(!) Я поместил восклицательный знак после третьего вызова для того, чтобы еше раз привлечь ваше внимание к тому факту, что вы можете обеспечить свой класс собственной версией размещающего оператора new, несмотря на то, что не можете заменить глобальную версию этого оператора. Сюрприз сокрытия имен Весь изложенный материал приводит нас к основной причине появления этого материала в книге, а именно - проблеме сокрытия имен. 3. Какой именно оператор new вызывается в приведенном далее коде в строках, пронумерованных от 1 до 4? пример 22-3: сокрытие имени new class Base { public: static void* operator new(std::size t, const FastMemory&); class Derived : public Base { ]* Derived* pi = new Derived; 1 Derived* p2 = new (std::nothrow) Derived; 2 void* p3 = /* некоторая область памяти, достаточная для размещения Derived */ ; new (рЗ) Derived; 3 FastMemory f; Derived* p4 = new (f) Derived; 4 Большинство из нас знакомы с проблемой сокрытия имен в других контекстах, когда имя в производном классе скрывает имя в базовом классе, но следует помнить, что сокрытие имен может проявиться и при работе с оператором new. Вкратце вспомним, как работает поиск имен: компилятор начинает поиск в текущей области видимости (в нашем случае - в области видимости Derived) и ищет требуемое имя (в нашем случае - operator new); если ни одного экземпляра искомого имени не найдено, он переходит к охватывающей области видимости (области видимости Base, а затем глобальной области видимости) и повторяет поиск. Как только найдена область видимости хотя бы с одним именем (в нашем случае - область видимости Base), поиск прекращается и работа продолжается только в плане
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |