![]() Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь 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; просмотров: 202; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.145.10.108 (0.009 с.) |