Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 52
Текст из файла (страница 52)
Ниже представлена именно эта форма оператораnew:указатель = new(nothrow)TIOI;Здесь указатель — это указатель на переменную типа тип. Форма оператораnew с ключевым словом nothrow (без вызова исключительной ситуации)функционирует аналогично его прежней, изначальной версии. Поскольку вэтом случае при неудачной попытке выделения памяти возвращается нуль,оператор new может быть легко "встроен" в старые программы и вам ненужно заботиться о формировании процедуры обработки какой бы то нибыло исключительной ситуации. Тем не менее, в новых программах механизм исключительных ситуаций предоставляет вам гораздо более широкиевозможности.ПримерыРI.
В представленном ниже примере с оператором new использование блокаtry/catch дает возможность проконтролировать неудачную попытку выделения памяти.^include <iostream>tfinclude <new>using namespace std;int main(){int *p;{p = new int; // выделение памяти для целого} catch (taad_alloc xa) {cout « "Ошибка выделения памяти\п";tryГлава11.Шаблоныиобработкаисключительныхситуаций_353return 1;for(*p = 0; *р < 10; (*р}-НОcout « *р « " ";delete p; // освобождение памятиreturn 0;В данном примере, если при выделении памяти случается ошибка, она перехватывается инструкцией catch.2. Поскольку в предыдущей программе при работе в нормальных условияхошибка выделения памяти чрезвычайно маловероятна, в представленномниже примере для демонстрации возможности возбуждения исключительнойситуации оператором new ошибка выделения памяти достигается принудительно.
Процесс выделения памяти длится до тех пор, пока не произойдетошибка.^include <iostream>^include <new>using namespace std;int raainO,double *p;// цикл будет продолжаться вплоть до исчерпания ресурса памятиdo {try {p = new double [100000];} catch (bad_alloc xa) (cout « "Ошибка выделения памяти\п";return 1;}cout « "Выделение памяти идет нормально\п";} while (p) ;return 0;3, В следующей программе показано, как использовать альтернативную формуоператора new — оператор new(nothrow). Это переработанная версия предыдущей программы с принудительным возбуждением исключительной ситуации.// Демонстрация работы оператора new(nothrow)^include <iostream>^include <new>Самоучитель C++354using namespace std;int main(){double *p;// цикл будет продолжаться вплоть до исчерпания ресурса памятиdo {р = new(nothrow) double[100000];i f ( p ) cout « "Выделение памяти идет нормально\п";else cout « "Ошибка вьщеления памяти\п";} while(р);return 0;}Как показано в этой программе, при использовании оператора new с ключевым словом nothrow, после каждого запроса на выделение памяти следуетпроверять возвращаемое оператором значение указателя.1.
Объясните, в чем разница между функционированием операторов new иnew(nothrow), если при выделении памяти происходит ошибка.2. Дан следующий фрагмент программы. Приведите два переделанных вариантаэтого фрагмента с учетом современных требований к программам на C++.р = malloc(sizeof(int));ifUp) {cout « "Ошибка выделения памяти\п";exit(l);роверка усвоенияматериала главыТеперь вам необходимо выполнить следующие упражнения и ответить навопросы.1. Создайте родовую функцию, возвращающую значение элемента, которыйчаще всего встречается в массиве.Глава 11.
Шаблоны и обработка исключительных ситуаций3552. Создайте родовую функцию, возвращающую сумму значений элементовмассива.3. Создайте родовой класс для "пузырьковой" сортировки (или используйтелюбой другой известный вам алгоритм сортировки).4. Измените класс stack так, чтобы в стеке можно было хранить пары объектов разных типов.5. Напишите обычные формы инструкций try, catch и throw. Опишитесвоими словами их функции.6. Еще раз измените класс stack так, чтобы переполнение и, наоборот,опустошение стека обрабатывались как исключительные ситуации.7.
Просмотрите документацию на ваш компилятор, Проверьте, поддерживает ли он функции terminate() и unexpected(). Как правило, эти функцииможно конфигурировать так, чтобы из них вы могли вызвать любую необходимую вам функцию. Если в случае с вашим компилятором это так,постарайтесь создать собственный набор функций завершения программы, который обеспечил бы возможность обработки необрабатываемых доэтого исключительных ситуаций.8. Философский вопрос: в чем, по вашему мнению, при неудачной попыткевыделения памяти преимущество возбуждения исключительной ситуацииоператором new по сравнению с возвращением нуля?Проверка усвоенияматериала в целомВ этом разделе проверяется, хорошо ли вы усвоили материал этой и предыдущей глав.1. В главе 6, раздел 6.7, пример 3 был представлен класс с безопасным массивом.
Переделайте его в родовой класс с безопасным массивом.2. В главе 1 были созданы перегруженные версии функции abs(). Усовершенствуйте решение, создав родовую функцию abs(), которая возвращалабы абсолютную величину любого численного объекта.Глава 12Динамическаяидентификацияи приведение типовВ этой главе рассказывается о двух сравнительно новых инструментах C++:динамической идентификации типа (Run-Time Type Identification, RTTI) иновых, более совершенных операторах приведения типов (casting operators).Динамическая идентификация типа дает возможность определить тип объекта во время выполнения программы. Новые операторы приведения типовпредоставляют более безопасные и управляемые способы выполнения операций приведения типов, по сравнению с существовавшими ранее.
Как выувидите в дальнейшем, один из операторов приведения типов, а именнооператор dynamic_cast, относится непосредственно к RTTI, поэтому имелосмысл объединить эти две темы в одной главе.Повторение пройденногоПеред тем как продолжить, необходимо правильно ответить на следующиевопросы и сделать упражнения.1. Что такое родовая функция и какова ее основная форма?2.
Что такое родовой класс и какова его основная форма?3. Напишите родовую функцию gexpQ, возвращающую значение одного изсвоих аргументов, возведенного в степень другого.4. В главе 9, раздел 9.7, пример 1 был создан класс coord для хранения целочисленных координат. Создайте родовую версию этого класса, чтобыможно было хранить координаты любого типа. Продемонстрируйте программу решения этой задачи.5. Кратко объясните, как совместная работа инструкций try, catch и throwобеспечивает в C++ обработку исключительных ситуаций.6. Можно ли использовать инструкцию throw, если ход выполнения программы не затрагивает инструкции, расположенные в блоке try?7. Для чего служат функции terminate!) и unexpectedQ?8.
Какая форма инструкции catch будет обрабатывать все типы исключительных ситуаций?358Самоучитель C++12.1. Понятие о динамическойидентификации типаПоскольку динамическая идентификация типа не характерна для языковпрограммирования, в которых не поддерживается полиморфизм (например,С), это понятие может оказаться для вас неизвестным. В языках, в которыхне поддерживается полиморфизм, информация о типе объекта во время выполнения программы просто не нужна, так как тип каждого объекта известен уже на этапе компиляции программы (вернее даже тогда, когдапрограмма еще пишется).
С другой стороны, в языках, поддерживающихполиморфизм (таких, как C++), возможны ситуации, в которых тип объектана этапе компиляции неизвестен, поскольку до выполнения программы неопределена точная природа объекта. Как вы знаете, в C++ полиморфизмреализуется через иерархии классов, виртуальные функции и указатели базовых классов. При таком подходе указатель базового класса может использоваться либо для указания на объект базового класса, либо для указания наобъект любого класса, производного от этого базового. Следовательно, невсегда есть возможность заранее узнать тип объекта, на который будет указывать указатель базового класса в каждый данный момент времени. В такихслучаях определение типа объекта должно происходить во время выполнения программы, а для этого служит механизм динамической идентификациитипа.Информацию о типе объекта получают с помощью оператора lypeid.
Дляиспользования оператора typeid в программу следует включить заголовок<typemfo>. Ниже представлена основная форма оператора typeid:typeid(объект)Здесь объект — этот тот объект, информацию о типе которого необходимополучить.
Оператор typeid возвращает ссылку на объект типа type_info, который и описывает тип объекта объект. В классе type_info определены следующие открытые члены:bool operator= (const type_info йобъвкт) ;bool operator!=(const type_info &объект);bool before(const type_info ^объект);const char *name();Сравнение типов обеспечивают перегруженные операторы == и !=. Функция before() возвращает истину, если вызывающий объект в порядке сортировки расположен раньше объекта, заданного в качестве параметра.
(Этафункция обычно предназначена только для внутреннего использования. Еевозвращаемое значение вряд ли может пригодиться при операциях с наследованием или иерархиями классов.) Функция пате() возвращает указательна имя типа.Глава 12. Динамическая идентификация и приведение типов359Хотя оператор typeid позволяет получать типы разных объектов, наиболееполезен он будет, если в качестве его аргумента задать указатель полиморфного базового класса. В этом случае оператор автоматически возвращает типреального объекта, на который указывает указатель.
Этим объектом можетбыть как объект базового класса, так и объект любого класса, производногоот этого базового. (Вспомните, указатель базового класса может указыватьлибо на объект базового класса, либо на объект любого класса, производного от этого базового.) Таким образом, с помощью оператора typeid вовремя выполнения программы можно определить тип объекта, на которыйуказывает указатель базового класса. То же самое относится и к ссылкам.Когда в качестве аргумента оператора typeid указана ссылка на объект полиморфного класса, оператор возвращает тип реального объекта, на которыйимеется ссылка.