Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 [ 45 ] 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

Рассмотрим пример, показывающий применение описанных версий 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), поиск прекращается и работа продолжается только в плане



выбора наилучшего соответствия среди найденных имен; эго означает, что остальные охватывающие области видимости (в данном случае - глобальная область видимости) более не рассматриваются и все функции в них оказываются скрыты. Далее компилятор просматривает все обнаруженные имена, выбирает функцию при помоши разрешения перегрузки и проверяет права доступа к ней, чтобы выяснить, может ли найденная функция быть вызвана в данном контексте. Внешние области видимости игнорируются, даже если ни одна из найденных перегруженных функций не имеет подходящей сигнатуры, т.е. ни одна из них не может быть вызвана. Внешние области видимости также игнорируются, если функция с соответствующей вызову сигнатурой не может быть вызвана из-за недоступности. (Более детально вопросы поиска и сокрытия имен рассмотрены в [SutterOO].)

Это означает, что если класс (или любой из его базовых классов) содержит собственный оператор new с любой сигнатурой, то эта функция скрывает все глобальные операгоры new и вы не имеете возможности записать обычное выражение с операго-ром new. в котором будет использоваться глобальная версия оператора. Вот что это обозначает в контексте примера 22-3.

Derived* pl = new Derived; Ошибка: соответствия нет

Derived* р2 =

new(std:rnothrow) Derived; Ошибка: соответствия нет

void* рЗ = /* некоторая область памяти, достаточная

для размещения Derived */ ; new (рЗ) Derived; ошибка: соответствия нет

FastMemory f; Derived* р4 =

new(f) Derived; вызов Base::operator new()

Ho что если мы хотим использовать глобальные версии операторов, как минимум в первых двух случаях, там, где перегрузка в базовом классе приводит к ошибке? Единственный разумный способ обеспечить возможность классу Derived использовать глобальные операторы new - это предоставить соответствующие функции в классе, которые просто передают вызов глобальным операторам (в противном случае в коде надо использовать квалифицированные имена :: new, чтобы компилятор использовал глобальные версии операторов).

Это приводит к интересному выводу, который лучше всего выразить в виде рекомендации (см. также [Meyers97]).

> Рекомендация

Если вы предоставляете хотя бы один специфичный для данного класса оператор new, следует также обеспечить класс обычным (без дополнительных параметров) оператором new.

Специфичная для класса версия оператора почти всегда должна сохранять семантику глобальной версии, поэтому следует объявлять ее со спецификацией исключений throw(std::badalioc), а реализовывать се желательно с использованием глобальной версии - если только вам действительно не нужны некоторые специфические действия оператора new.

предпочтительная реализация оператора new, специфичного

для класса

void* C::operator newCstd: :size .t s) throw(std: :bad a!loc) { return ::operator new( s );



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 [ 45 ] 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85