Анимация
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 вместо используемой по умолчанию, но обычно это именно то, что требуется: в большинстве случаев замещенный глобальный оператор new используется для отладки или контроля за использованием памяти, и такое поведение желательно отразить и в специфичных для классов версиях new.

Если же вы не хотите поступать таким образом, то ваш класс будет невозможно использовать кодом, который будет пытаться динамически распределять память для объектов обычным способом.

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

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

Этот оператор должен сохранять семантику глобальной версии, поэтому его следует объявлять как не генерирующий исключений и реализовать посредством глобальной версии.

предпочтительная реализация размещающего оператора new,

специфичного для класса

void* С;:operator new( std::size t s, void* p ) throwC) { return ::operator new( s, p );

Если вы этого не сделаете, то не сможете работать с кодом, который использует размещающий оператор new с вашим классом. В частности, реализации контейнеров стандартной библиотеки часто используют размещающие конструирование объектов и ожидают, что оно будет работать, как обычно; в конце концов, это единственный способ явного вызова конструктора в С++. Если вы не напишете такой оператор new, то, скорее всего, вы не сможете воспользоваться даже std: :vector<C>.

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

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

Реализовать этот оператор можно с использованием глобального не генерирующего исключений new.

вариант А реализации не генерирующего исключений

оператора new класса

void* C::operator new(std::size t s, const std::nothrow t& n) throwO { return ::operator new( s, n ); }

Другой вариант реализации использует обычный оператор new класса (выполняется то же, что глобальным не генерирующим исключений оператором new; однако при этом мы застрахованы от изменения глобального оператора другим программистом):

вариант Б реализации не генерирующего исключений

оператора new класса

void* С::operator

new(std: :size t s, const std: :nothrow..t&) throwO



try {

return С::operator new( s );

catchC ... ) { return 0;

Заметим, что рассмотршные тартат« тзово* тую5алънъ\х операторов иеволиож-но реализовать при помощи using-объявления, такого как using •.-.operator new,. Единственное место, где такое объявление может оказаться полезным - в описании класса с. В пределах класса можно использовать только using-объявления, которые вносят имена из базовых классов, но не такие, как имена из глобальной области видимости или имена из других классов. Требование, чтобы вызывающий код сам добавлял using-объяаления, не только обременительно, но и бесполезно, поскольку мы можем быть не в состоянии сго изменять. Часть кода может находиться в модулях, доступных только для чтения, как, например, библиотеки сторонних производителей, или даже внутри стандартной библиотеки С++, если мы попытаемся обеспечить стандартные контейнеры доступом к специфичному для класса размещающему оператору new.

Резюме

Если вы предоставляете хотя бы один специфичный для данного класса оператор new, то:

• следует также обеспечить класс обычным (без дополнительных параметров) оператором new;

• следует также обеспечить класс размещающим оператором new;

• следует также обеспечить класс оператором new, не генерирующим исключений, на тот случай, если пользователи вашего класса захотят им воспользоваться; в противном случае этот оператор окажется скрытым другими перегрузками оператора new в данном классе.

В следующей задаче мы более подробно разберемся, что же означает сбой оператора new и как лучше всего обнаружить и обработать его. Мы также увидим, что в своих программах желательно избегать применения new(nothrow), но что еще более неожиданно, мы обнаружим, что у ряда популярных платформ отказы при распределении памяти не сообщают о себе так, как того требует стандарт.



Задача 23. Новый взгляд на new.

Часть 2: прагматизм в управлении памятью Сложность: 5

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

Вопрос для новичка

1. Опишите два основных стандартных способа сообщения об ошибке оператором new в случае нехватки памяти.

Вопрос для профессионала

2. Помогает ли использование не генерирующей исключения версии оператора new сделать код более безопасным с точки зрения исключений? Обоснуйте ваш ответ.

3. Опишите реальные ситуации - в пределах стандарта С++ или вне его - когда проверка исчерпания памяти невозможна или бесполезна.

Решение

в предыдущей задаче я проиллюстрировал и обосновал следующую рекомендацию.

• Любой класс, предоставляющий собственный оператор new или new[], должен также предоставлять соответствующие специфичные для данного класса версии обычного оператора new, размещающего оператора new и оператора new, не генерирующего исключений. В противном случае у пользователей вашего класса могут возникнуть лишние проблемы.

Сейчас мы уделим наше внимание вопросу о том, что же означает отказ оператора new и как лучше всего обнаружить его и обработать.

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

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

Здесь я опять для простоты не буду отдельно рассматривать оператор new для массивов. Все, что будет сказано об операторе new для одного объекта, относится и к оператору для массивов.

Исключения, ошибки и new(nothrow)

1. Опишите два основных стандартных способа сообщения об ошибке оператором new в случае нехватки пштш.

В то время как остальные разновидности оператора new сообщают об ошибках, генерируя исключение badalloc, не генерирующий исключений оператор new сообщает о них проверенным временем способом - как и функция malloc, он возвращает нулевой указатель. Этим гарантируется, что такой оператор, как следует из его названия, никогда не генерирует исключений.

Главный вопрос - дает ли это нам что-нибудь или нет? Ряд программистов ошибочно полагали, что использование не генерирующего исключений оператора new увеличивает безопасность программы, поскольку уменьшает количество возможных исключений. Но так ли это на самом деле?



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