
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
- •Оглавление
- •Предисловие
- •Введение
- •1.1. Понятие класса и объекта. Инкапсуляция
- •1.2. Определение классов. Компоненты. Доступность
- •Class_key /*class_id*/ { /*members_list*/ };
- •Value_type class_id::function_id(parameters) {statements}
- •CPoint point1(100,70); // локальный объект
- •Static cPoint point3(50,120); // статический объект
- •Class_id(parameters) /*:initializer_list*/ {/*statements*/}
- •CString(const char *);
- •Delete[] __thematrix;
- •1.4. Обращение к компонентам объектов
- •1.5. Статические и нестатические компоненты классов
- •1.7. Указатель this
- •В опросы для самопроверки
- •2. Механизм наследования. Полиморфизм
- •2.1. Формы наследования. Базовые и производные классы
- •Class_key class_id: inheritance_specifier base_class_id {member_list};
- •2.3. Абстрактные классы
- •2.4. Множественное наследование и виртуальные классы
- •2.5. Преобразование динамических типов. Динамическая идентификация типов
- •Catch ( std::bad_cast & ) { // обработка исключения
- •Return 0;
- •Вопросы для самопроверки
- •3. Дружественные функции и классы
- •3.1. Дружественные функции
- •3.2. Дружественные классы
- •Вопросы для самопроверки
- •4. Механизм вложения
- •4.1. Вложенные классы
- •4.2. Локальные классы
- •Вопросы для самопроверки
- •5. Объектная модель и шаблоны
- •5.1. Определение, описание и инстанцирование шаблонов
- •::Function_id(function_parameter_list) { statements }
- •5.2. Параметры и аргументы шаблонов
- •Class identifier typename identifier
- •// Key, Data – параметры-типы (типы ключа и данных отображения)
- •// Container – контейнер, где содержится информация отображения class сMap {
- •Class MyTemplate
- •Int array[10]; struct Structure { int m; static int sm; } str;
- •5.3. Шаблоны компонентных функций
- •Value_type function_template_id(function_parameter_list) { statements }
- •::Function_template_id(function_parameter_list) { statements }
- •5.4. Специализация шаблонов
- •Вопросы для самопроверки
- •6. Перегрузка операций
- •Value_type operator @ (parameter_list);
- •Value_type operator @ (parameter_list) { statements }
- •Return fail();
- •6.3. Перегрузка бинарных операций
- •Value_type operator @ (parameter); // компонентная функция
- •Value_type operator @ (parameter, parameter); // глобальная функция friend value_type operator @ (parameter, parameter); // дружественная функция
- •Return *this;
- •Return *this;
- •/* Присваиваем собственные данные класса d */
- •6.4. Перегрузка операций управления памятью
- •Typedef void (*new_handler) ();
- •Extern new_handler set_new_handler( new_handler new_p );
- •Void operator delete(void * memory) {
- •... // Специальная обработка пользователя ::operator delete(memory); // освободить память
- •Вопросы для самопроверки
- •7. Механизм исключений
- •Throw expression
- •7.3. Специальные средства поддержки механизма исключений
- •Unexpected_function set_unexpected(unexpected_function func_name);
- •Typedef void (* unexpected_function) ();
- •Extern char * __throwExceptionName; extern char * __throwFileName; extern unsigned __throwLineNumber;
- •Вопросы для самопроверки
- •8. Подсчет ссылок
- •8.1. Назначение механизма подсчета ссылок
- •8.2. Контекстно-независимая модель счетчика ссылок
- •8.4. Внедрение подсчета ссылок в существующий класс
- •Вопросы для самопроверки
- •9. Стандартная библиотека шаблонов (stl)
- •9.1. Назначение и архитектура stl
- •9.2. Последовательные контейнеры
- •Class vector {
- •// Определение итераторов
- •Sort(first,last); // сортировка вектора в диапазоне итераторов
- •Ifstream ifile ("example.In"); ofstream ofile ("example.Out");
- •OutputIterator copy(
- •InputIterator first, InputIterator last, OutputIterator result );
- •// Заполнение списка
- •Operator- (int)
- •Operator- (random access iterator) operator[] (int)
- •InputIterator find(InputIterator first, InputIterator last, const t & value);
- •InputIterator find(InputIterator first, InputIterator last, const t & value)
- •Return first;
- •OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
- •Return result;
- •OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
- •Return result;
- •Void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp)
- •__Quick_sort_loop(first, last, comp); __final_insertion_sort(first, last, comp);
- •T accumulate(InputIterator first, InputIterator last, t init, Function f);
- •V.Push_back(2); V.Push_back(5);
- •9.5. Функторы
- •T operator()(const t & X) const { return -X; }
- •9.7. Адаптеры
- •S1.Push(1); s1.Push(5);
- •// Записать в вектор числа 1 2 3 4
- •// Сортировать по неубыванию
- •// Записать в вектор числа 4 6 10 3 13 2
- •Вопросы для самопроверки
- •Заключение
- •Библиографический Список
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
6.4. Перегрузка операций управления памятью
С++ позволяет программисту реализовать собственные варианты управления памятью и обработки ошибок путем перегрузки операций выделения и освобождения динамической памяти. Выделение памяти в С++ выполняет функция с названием 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: