Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Double operator()(double,double); // прототип операцииСодержание книги
Поиск на нашем сайте
private: ... }; // внешнее определение операции double Function::operator()(double a, double b) { ... } // использование класса Function Function f; double z = f(2.5,5.6); // операционная форма вызова double w = f.operator()(2.5,5.6); // функциональная форма вызова Функция operator() может иметь параметры со значениями по умолчанию. Также допускается наличие в одном и том же классе нескольких перегруженных вариантов функции operator(). Их выбор осуществляется по порядку и типам параметров, как и в случае обычной перегрузки функций. Перегрузка операций управления памятью С++ позволяет программисту реализовать собственные варианты управления памятью и обработки ошибок путем перегрузки операций выделения и освобождения динамической памяти. Выделение памяти в С++ выполняет функция с названием operator new (не путать с операцией new), а освобождение памяти – функция operator delete (не путать с delete). Для массивов выделение и освобождение памяти выполняют соответственно функции operator new[] и operator delete[] (также не путать с new[] и delete[]). Эти функции неявно вызываются операциями new, delete, new[], delete[] для соответствующих манипуляций с памятью. Так, операция new сначала обращается к функции operator new, получает от нее указатель на выделенный блок памяти и затем вызывает конструктор класса для инициализации объекта в этом блоке. Операция delete сначала вызывает деструктор класса для деинициализации объекта, а затем обращается к функции operator delete для освобождения памяти, которую занимал объект. Похожая ситуация характерна и для new[], delete[][37]. Программисту разрешено перегружать функции operator new, operator delete, а также operator new[] и operator delete[]; перегрузка соответствующих операций new, delete, new[], delete[] не допускается. Прототипы функций operator new, operator delete, operator new[] и operator delete[] сосредоточены в заголовочном файле new.h. Для правильной перегрузки функции выделения динамической памяти new следует придерживаться ряда правил: 1) обеспечивать правильное возвращаемое значение; 2) корректно обрабатывать выделение блока памяти нулевого и «неправильного» размера; 3) осуществлять вызов функции обработки ошибок при нехватке памяти. Функция operator new должна иметь первый параметр типа size_t и может иметь дополнительные параметры; для них даже допустимы умалчиваемые значения. Правильное возвращаемое значение функции operator new определить легко. В случае успеха это указатель на блок выделенной памяти; в случае неудачи исключение std::bad_alloc, которое выбрасывается стандартной функцией выделения памяти. Если в функцию передается запрос на блок памяти нулевого размера, то его можно просто принудительно «превращать» в запрос блока размером 1 байт. При неправильном размере блока обработку запроса следует просто передавать стандартной функции::operator new. Обработку ошибок в operator new можно организовать за счет переопределения стандартной функции обработки ошибок. Эта функция описана в файле new.h и определяется типом new_handler: typedef void (*new_handler) (); Переопределить рассматриваемую функцию можно с помощью функции set_new_handler, которая также описана в new.h и имеет следующий прототип: extern new_handler set_new_handler(new_handler new_p); Функции set_new_handler должен быть передан указатель типа new_handler на новую функцию-обработчик, при этом она возвратит указатель на старую функцию-обработчик. Ниже приводится определение и реализация класса NewHandler, который инкапсулирует процесс выделения памяти и обработки ошибок с учетом описанных выше правил. Наследование от этого класса позволяет инкапсулировать управление динамической памятью в любом пользовательском классе. // заголовочный файл class NewHandler { // управление выделением памяти public: // замена обработчика ошибок static new_handler set_new_handler(new_handler new_new_handler); // выделение памяти static void * operator new(size_t sz); private: // текущий обработчик ошибок static new_handler current_handler; }; // файл реализации // обязательная инициализация статического компонента new_handler NewHandler::current_handler; // новый (пользовательский) обработчик ошибок void MyNewHandlerFunc() { Специальная обработка пользователя throw std::bad_alloc(); } // пользовательская функция выделения памяти void * NewHandler::operator new(size_t sz) { sz = sz? sz: 1; // проверяем размер на нуль void * mem_block = 0; // ставим новый обработчик ошибок и запоминаем указатель на старый new_handler old_handler = set_new_handler(MyNewHandlerFunc); try { // пытаемся выделить память mem_block =::operator new(sz); // специальная обработка пользователя ... } catch(std::bad_alloc &) { // попытка неудачна set_new_handler(old_handler); // восстанавливаем старый обработчик Throw; // передаем исключение старому обработчику } // восстанавливаем старый обработчик set_new_handler(old_handler); // возвращаем указатель на выделенный блок return mem_block; } // замена обработчика ошибок new_handler NewHandler::set_new_handler(new_handler new_new_handler) { new_handler old_handler =::set_new_handler(new_new_handler); current_handler = new_new_handler; return old_handler; } Функция обработки ошибок может быть специфицирована явно в заголовке функции operator new. Тогда устанавливать обработчик ошибок можно сразу при вызове operator new. Ниже дано модифицированное определение класса NewHandler и функции operator new, допускающее явное задание обработчика ошибок: class NewHandler { public: // замена обработчика ошибок static new_handler set_new_handler(new_handler new_new_handler); // выделение памяти static void * operator new(size_t sz, new_handler pnew_handler); private: // текущий обработчик ошибок static new_handler current_handler; }; ... void * NewHandler::operator new(size_t sz, new_handler pnew_handler) { sz = sz? sz: 1; void * mem_block = 0; new_handler old_handler = set_new_handler(pnew_handler); То же, что и выше } Перегрузка функции operator new[] в целом аналогична перегрузке operator new. Аналогичны и правила перегрузки, однако имеется ряд жестких ограничений. Для функции освобождения динамической памяти operator delete есть всего два правила перегрузки: 1) корректно «освобождать» память по нулевому указателю; 2) правильно реагировать на ситуацию, когда удаляемый объект имеет некорректный размер. Вот обобщенный вариант переопределения функции operator delete: void operator delete(void * memory) { if(memory == 0) return; // для нулевого указателя ничего не делать Специальная обработка пользователя ::operator delete(memory); // освободить память ... } Операция operator delete[] перегружается аналогично, но, как и operator new[], характеризуется жесткими ограничениями.
Вопросы для самопроверки
1. Опишите сущность механизма перегрузки стандартных операций. 2. Перечислите основные ограничения перегрузки операций. 3. Как можно определить функцию-операцию по отношению к классу, для которого выполняется перегрузка операции? 4. Как должен в общем виде выглядеть прототип функции-операции, перегружающей префиксную унарную операцию? 5. Почему операцию = невозможно перегрузить глобальной функцией? Обоснуйте ответ. 6. Прототип функции-операции, суммирующей две матрицы, имеет вид const Matrix & operator + (const Matrix & m2) const; Какая в нем имеется ошибка и каковы ее потенциальные последствия? 7. Прототип функции-операции присваивания, определенной для класса матриц, имеет вид Matrix & operator = (const Matrix & m) const; Какого рода ошибка содержится в приведенном прототипе? 8. Требуется реализовать операцию суммирования матриц класса CMatrix с вещественным выражением с сохранением свойства коммутативности операции. Как следует определить эту функцию-операцию? Дайте эскиз определения. 9. Как правильно построить заголовок функции-операции operator –, которая изменяет знак всех элементов матриц класса CMatrix на противоположный? 10. Почему функция operator = в общем случае должна проверять присваивание объекта самому себе? Когда такая проверка избыточна? 11. Опишите алгоритм работы функции operator =, если класс, для которого она перегружена, является производным от другого класса. 12. Пусть для класса CRationalNumber, представляющего рациональные числа, требуется определить функцию operator <<. Как это сделать наиболее грамотно? 13. Как правильно задать заголовок функции operator <<, чтобы были возможны операторы вида cout << m1 << m2 << m3, где m1,m2,m3 – объекты класса, для которого определяется функция operator<<? 14. Можно ли операцию [] перегрузить дружественной функцией? Поясните ответ. 15. Почему операция operator [] обычно перегружается дважды? 16. В классе Matrix, описывающем прямоугольные матрицы, имеется функция const Matrix operator + (double r) const; Она увеличивает элементы матрицы на выражение r. Можно ли этой функцией вычислить выражение 0.5+m? Обоснуйте ответ. 17. Программа содержит оператор m1 = m1 + m2, где m1 и m2 – объекты некоторого класса C, для которого перегружены операции = и +. Перепишите этот оператор в эквивалентной функциональной форме. 18. Чем отличаются заголовки функций-операций пре-инкремента (++c) и постинкремента (c++)? 19. Почему функция-операция префиксного декремента (--c) должна возвращать ссылку на класс? Обоснуйте ответ. 20. Заголовок операции суммирования, определенной для класса матриц, имеет вид: virtual const Matrix operator + (const Matrix & m2) const; Как правильно переопределить эту операцию в производном классе, например, поименованных матриц? Приведите только заголовок и обоснуйте выбранный вариант. 21. В базовом классе BasicMatrix, представляющем матрицы, определена операция вывода в поток с заголовком: friend ostream & operator << (ostream & stream, const BasicMatrix & m); От класса BasicMatrix порожден производный класс NamedMatrix, описывающий поименованные матрицы. Как наиболее рационально реализовать операцию записи в поток для этого производного класса? 22. Для класса NamedMatrix, являющегося производным от класса Matrix, определена операция чтения из потока: istream & operator >> (istream & stream, Matrix & m) { return stream >> dynamic_cast<Matrix &>(m); } Какого рода ошибка есть в этом определении? Каковы “симптомы” ее проявления? 23. В классе, представляющем двоичные векторы BoolVector, определена операция индексации с заголовками bool & operator [] (int i); (вариант 1) и const bool operator [] (int i) const; (вариант 2). Какой из вариантов операции будет использован в операторе cout<<v[i], если вектор v определен как неконстантный объект класса BoolVector? Обоснуйте ответ. 24. В классе, представляющем двоичные векторы BoolVector, определена операция с заголовком const BoolVector & operator | (const BoolVector & v) const; обеспечивающая поэлементное ИЛИ над векторами-операндами. Какого рода ошибка заключена в приведенном заголовке? 25. Запишите заголовок функции-операции явного преобразования объекта класса CClass2 в ранее определенный класс CClass1. 26. Чем вызвана невозможность корректной перегрузки операций && и ||? Почему операции & и | в отличие от первых двух можно легко перегрузить корректно? Дайте обоснование. 27. Приведите пример определения класса функциональных объектов (функторов). 28. Имеется прототип функции-операции: double operator () (double, double, double,...)? Покажите как можно вызвать эту функцию. 29. В классе функторов CRandomGenerator (генератор случайных чисел с равномерным распределением на отрезке [a,b]) определена лишь одна следующая операция: double operator () (double a, double b) { randomize(); return a + fabs(b - a) * random(10000) / 10000; } Как воспользоваться функциональностью этого класса в программе (получить случайное число)? Приведите фрагмент кода. 30. Каким образом можно перегрузить функцию operator new? Опишите общие принципы перегрузки. 31. В чем состоит отличие между операциями new и operator new, delete и operator delete? 32. Как можно установить специальный обработчик ошибок выделения памяти операцией new? Ответ поясните примером. 33. В программе есть оператор вида CNamedArray * parr = new (MyNewHandlerFunc) CNamedArray("array", array, sizeof(array)/sizeof(array[0])); Он создает в динамической памяти объект класса CNamedArray (поименованный массив). Что представляет из себя имя MyNewHandlerFunc и как должна выглядеть его декларация?
Задачи
Решить задачи 1, 2 главы 5, используя возможности механизма перегрузки операций[38]. Использовать по возможности как компонентные, так и глобальные функции-операции.
Механизм исключений Исключения (исключительные ситуации) – это особые состояния вычислительного процесса, которые требуют специальной обработки. Они могут быть связаны с внутренними ошибками программы или с условиями (не обязательно ошибочными), которые искусственно формируются по указанию программиста. Отличие исключительных ситуаций от просто ошибок состоит в том, что в момент их возникновения нормальное выполнение программы прекращается и управление немедленно передается в ближайший по вложенности участок, который реализует их обработку, независимо от контекста, где возникло исключение. Если обработка исключения не выполняется, то происходит принудительное завершение программы. Механизм исключений в С++ достаточно тесно связан с объектным подходом и поэтому рассматривается как подраздел в рамках объектно-ориентированного программирования на С++. 7.1. Модель исключений в С++. Синтаксис Для реализации обработки исключений С++ содержит три служебных слова: try, catch, throw. Служебное слово try позволяет выделить в любом месте программы контролируемый участок (блок), т.е. блок, где потенциально возможно возникновение исключительных ситуаций. Синтаксис его применения следующий: try { statements } После возникновения исключения в контролируемом блоке управление немедленно передается за его пределы, где должны быть размещены обработчики исключений. Каждый обработчик идентифицируется служебным словом catch и записывается в следующем формате: catch (declaration) { statements } где declaration определяет тип исключений, обрабатываемых данным обработчиком; в фигурных скобках записаны операторы блока обработки исключения. Пример try { // контролируемый блок DataObject.AllocateMemory(); InputStream >> DataObject; DataObject.Process(); OutputStream << DataObject; } catch(const char * e) // обработчик исключений типа const char * { /*... */ } В общем случае в контролируемом блоке могут формироваться исключения разных типов и, скорее всего, необходимо предусмотреть обработчик на каждый из них: try { statements }
|
||||
Последнее изменение этой страницы: 2016-08-14; просмотров: 198; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.137.215.202 (0.011 с.) |