
- •1.2 Философские замечания
- •1.3 Процедурное программирование
- •1.4 Модульное программирование
- •1.5 Абстракция данных
- •1.6 Пределы абстракции данных
- •1.7 Объектно-ориентированное программирование
- •1.8 Концепции объектно-ориентированного программирования
- •1.8.1 Инкапсуляция
- •1.8.2 Полиморфизм
- •1.8.3 Наследование
- •1.10 Несколько полезных советов
- •2.2 Перегрузка функций
- •2.3 Перегрузка операторов
- •2.4 Наследование
- •2.5 Конструкторы и деструкторы
- •2.7 Два новых типа данных
- •Глава 3. Классы и объекты
- •3.1 Параметризованные конструкторы
- •3.2 Дружественные функции
- •3.3 Значения аргументов функции по умолчанию
- •3.3.1 Корректное использование аргументов по умолчанию
- •3.4 Взаимосвязь классов и структур
- •3.5 Связь объединений и классов
- •3.6 Анонимные объединения
- •3.7 Inline-функции
- •3.7.1 Создание inline-функций внутри класса
- •3.8 Передача объектов в функции
- •3.9 Возвращение объектов функциями
- •3.10 Присваивание объектов
- •3.11 Конструктор копирования
- •3.12 Массивы объектов
- •3.12.1 Инициализация массивов объектов
- •3.12.2 Создание инициализированных и неинициализированных массивов
- •3.13 Указатели на объекты
- •3.14 Статические члены класса
- •Глава 4. Перегрузка функций и операторов
- •4.1 Перегрузка конструкторов
- •4.2 Локализация переменных
- •4.3 Локализация создания объектов
- •4.4 Перегрузка функций и неопределенность
- •4.5 Определение адреса перегруженной функции
- •4.6 Указатель this
- •4.7 Перегрузка операторов
- •4.8 Дружественная функция-оператор
- •4.9 Ссылки
- •4.9.1 Параметры-ссылки
- •4.9.2 Передача ссылок на объекты
- •4.9.3 Возврат ссылок
- •4.9.4 Независимые ссылки
- •4.9.5 Использование ссылок для перегрузки унарных операторов
- •4.10 Перегрузка оператора []
- •4.11 Создание функций преобразования типов
- •Глава 5. Наследование, виртуальные функции и полиморфизм
- •5.1 Наследование и спецификаторы доступа
- •5.1.1 Спецификаторы доступа
- •5.1.2 Спецификатор доступа при наследовании базового класса
- •5.1.3 Дополнительная спецификация доступа при наследовании
- •5.2 Конструкторы и деструкторы производных классов
- •5.3 Множественное наследование
- •5.4 Передача параметров в базовый класс
- •5.5 Указатели и ссылки на производные типы
- •5.6 Ссылки на производные классы
- •5.7 Виртуальные функции
- •5.8 Для чего нужны виртуальные функции?
- •5.9 Чисто виртуальные функции и абстрактные типы
- •5.10 Виртуальный базовый класс
- •5.11 Раннее и позднее связывание
- •Глава 6. Подсистема динамического выделения памяти
- •6.1 Введение в обработку исключений
- •6.1.1 Перехват всех исключений
- •6.2 Работа с памятью с помощью new и delete
- •6.3 Размещение объектов
- •6.4 Перегрузка new u delete
- •7.1.1 Потоки
- •7.3 Создание собственных операторов вставки и извлечения
- •7.3.1 Создание операторов вставки
- •7.3.2 Перегрузка операторов извлечения
- •7.4 Форматирование ввода/вывода
- •7.4.1 Форматирование с помощью функций-членов класса ios
- •7.4.2 Использование манипуляторов
- •7.5 Создание собственных функций-манипуляторов
- •7.5.1 Создание манипуляторов без параметров
- •7.5.2 Создание манипуляторов с параметрами
- •7.6 Файловый ввод/вывод
- •7.6.1 Открытие и закрытие файлов
- •7.6.2 Чтение и запись в текстовые файлы
- •7.6.3 Двоичный ввод/вывод
- •7.6.4 Определение конца файла
- •7.6.5 Произвольный доступ
- •Глава 8. Ввод/вывод в массивы
- •8.1 Классы ввода/вывода в массивы
- •8.2 Создание потока вывода
- •8.3 Ввод из массива
- •8.4 Использование функций-членов класса ios
- •8.5 Потоки ввода/вывода в массивы
- •8.6 Произвольный доступ в массив
- •8.7 Использование динамических массивов
- •8.8 Манипуляторы и ввод/вывод в массив
- •8.9 Собственные операторы извлечения и вставки
- •8.10 Форматирование на основе массивов
- •Глава 9. Шаблоны и библиотека stl
- •9.1 Функции-шаблоны
- •9.2 Функции с двумя типами-шаблонами
- •9.3 Ограничения на функции-шаблоны
- •9.4 Классы-шаблоны
- •9.5 Пример с двумя типами-шаблонами
- •9.6 Обзор библиотеки stl
- •9.7 Класс vector
- •9.7 Класс string
- •9.8 Класс list
6.1.1 Перехват всех исключений
В определенных обстоятельствах может потребоваться перехватывать все исключения, а не какой-то конкретный тип. Для этого достаточно использовать следующую форму инструкции catch:
catch(...)
{
// обработка всех исключений
}
Здесь многоточие соответствует любому типу данных.
Следующая программа иллюстрирует использование catch (...):
#include <iostream.h>
void x_handler(int test)
{
try
{
if(test==0) throw test; // генерация int
if(test==1) throw 'a'; // генерация char
if(test==2) throw 123.23; // генерация double
}
catch(...)
{
cout << "Caught exception!\n";
}
}
int main()
{
cout << "Start\n";
x_handler(0);
x_handler(1);
x_handler(2);
cout << "End";
return 0;
}
Программа выведет на экран следующий текст:
Start
Caught exception!
Caught exception!
Caught exception!
End
Как можно видеть, три инструкции throw были перехвачены с использованием одной инструкции catch.
6.2 Работа с памятью с помощью new и delete
Как известно, в языке С для динамического выделения и освобождения памяти используются функции malloc() и free(). Кроме них C++ содержит два оператора, выполняющих выделение и освобождение памяти более эффективно и более просто. Этими операторами являются new и delete. Их общая форма имеет вид:
переменная_указатель = new тип_переменной;
delete переменная_указатель;
Здесь переменная_указатель является указателем типа тип_переменной. Оператор new выделяет память для хранения значения типа тип_пережнной и возвращает ее адрес. С помощью new могут быть размещены любые типы данных. Оператор delete освобождает память, на которую указывает указатель переменная_указатель.
Если операция выделения памяти не может быть выполнена, то возможны два варианта поведения оператора new. Поведение по умолчанию состоит в том, что оператор new генерирует исключение типа bad_alloc. Второй вариант поведения new в случае ошибки состоит в том, что оператор new возвращает значение NULL, т.е. нулевой указатель. Поскольку второй вариант является нестандартным поведением оператора new, для его использования необходимо предварительно один раз вызвать функцию set_new_handler() с нулевым значением в качестве аргумента. Кроме того, необходимо включить в программу заголовочный файл new.h. В следующих примерах будут рассмотрены оба варианта обработки ошибок оператора new.
Оператор delete следует использовать только для указателей на память, выделенную с использованием оператора new. Использование оператора delete с другими типами адресов может породить серьезные проблемы.
Есть ряд преимуществ использования new перед использованием malloc(). Во-первых, оператор new автоматически вычисляет размер необходимой памяти. Нет необходимости в использовании оператора sizeof(). Более важно то, что он предотвращает случайное выделение неправильного количества памяти. Во-вторых, оператор new автоматически возвращает указатель требуемого типа, так что нет необходимости в использовании оператора преобразования типа. В-третьих, как скоро будет описано, имеется возможность инициализации объекта при использовании оператора new. И наконец, имеется возможность перегрузить оператор new и оператор delete глобально или по отношению к тому классу, который создается.
Ниже приведен простой пример использования операторов new и delete.
#include <iostream.h>
int main()
{
int *p;
try
{ // обрабатываем возможное исключение оператора new
p = new int; // выделение памяти для одной переменной типа int
}
catch(bad_alloc a) // также можно писать catch(...)
{
cout << "Allocation failure.\n";
return 1;
}
*p = 20; // присвоение данному участку памяти значения 20
cout << *p; // демонстрация работы путем вывода значения
delete p; // освобождение памяти
return 0;
}
Эта программа присваивает переменной p адрес блока памяти, имеющего достаточный размер для того, чтобы содержать число целого типа. Далее этой памяти присваивается значение 20 и содержимое памяти выводится на экран. Наконец, динамически выделенная память освобождается.
Как отмечалось, можно инициализировать память с использованием оператора new. Для этого надо указать инициализирующее значение в скобках после имени типа. Например, в следующем примере память, на которую указывает указатель p, инициализируется значением 99:
#include <iostream.h>
#include <new.h>
int main()
{
int *p;
set_new_handler(0); // оператор new будет возвращать NULL при ошибке
p = new int(99); // инициализация 99-ю
if(!p) { cout << "Allocation failure.\n"; return 1; }
cout << *p;
delete p;
return 0;
}
С помощью new можно размещать массивы. Общая форма для одномерного массива имеет вид:
переменная_указатель = new тип_переменной[размер];
Здесь размер определяет число элементов в массиве. Необходимо запомнить важное ограничение при размещении массива: его нельзя инициализировать.
Для освобождения динамически размещенного массива необходимо использовать следующую форму оператора delete:
delete [] переменная_указатель;
Здесь скобки [] информируют оператор delete, что необходимо освободить память, выделенную; для массива.
В следующей программе выделяется память для массива из 10 элементов типа float. Элементам массива присваиваются значения от 100 до 109, а затем содержимое массива выводится на экран:
#include <iostream.h>
int main()
{
float *p;
int i;
set_new_handler(0);
p = new float[10]; // выделение памяти для массива из 10 элементов
if(!p) { cout << "Allocation failure.\n" ; return 1; }
for(i=0; i<10; i++) p[i] = 100.00 + i;
for(i=0; i<10; i++) cout << p[i] << " ";
delete [] p; // удаление всего массива
return 0;
}