
- •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.3 Размещение объектов
Как отмечалось, можно выделить память для любого типа данных. Сюда включаются и объекты. Например, в следующей программе выделяет память для объекта класса point:
#include <iostream.h>
class point
{
int x, y, z;
public:
point(int a, int b, int c) { x=a; y=b; z=c; }
~point() { cout << "Destructing\n"; }
void show() { cout << x << “ “ << y << “ “ << z << “\n”; }
};
int main()
{
point *p;
set_new_handler(0);
p = new point(5, 6, 7);
if(!p) { cout << "Allocation failure.\n"; return 1; }
p->show();
delete p;
return 0;
}
Когда эта программа запускается, видно, что конструктор класса point вызывается в момент достижения строки, где стоит оператор new, а деструктор вызывается по достижении оператора delete. Заметим также, что инициализирующие значения автоматически передаются конструктору оператором new.
Ниже приведен пример, в котором размещается массив объектов типа point:
#include <iostream.h>
class point
{
int x, y, z;
public:
point(int a, int b, int c) { x=a; y=b; z=c; }
point() { cout << "Constructing\n"; }
~point() { cout << "Destructing\n"; }
void show() { cout << x << “ “ << y << “ “ << z << “\n”; }
void set(int a, int b, int c) { x=a; y=b; z=c; }
};
int main()
{
point *p;
int i;
set_new_handler(0);
p = new point[10];
if(!p) { cout << "Allocation failure.\n"; return 1; }
for(i=0; i<10; i++)
p[i].set(1, 2, 3);
for(i=0; i<10; i++)
p[i].show();
delete [] p;
return 0;
}
Обратим внимание, что к классу point добавлен второй конструктор. Поскольку динамический массив не может быть инициализирован, то требуется конструктор без параметров. Если такой конструктор не будет определен, то компилятор выдаст сообщение об ошибке.
6.4 Перегрузка new u delete
Имеется возможность перегрузить операторы new и delete. Это следует делать в том случае, если необходимо использовать какой-то особый способ выделения памяти. Например, может понадобиться процедура выделения памяти, автоматически использующая дисковый файл в качестве виртуальной памяти в том случае, когда куча оказывается исчерпанной. Какой бы ни была причина, осуществить такую перегрузку очень просто.
Для перегрузки операторов new и delete может использоваться следующий формат:
void* operator new(size_t размер)
{
// выполнение выделения памяти
return указатель_на_память;
}
void operator delete(void *p)
{
// освобождение памяти, на которую указывает р
}
Параметр размер будет содержать число в байтах, которое необходимо выделить для размещения объекта. Это значение будет сформировано автоматически. Перегруженная функция new должна возвращать указатель на выделенную память. За исключением этих ограничений перегруженная функция new может выполнять все, что необходимо.
Функция delete получает указатель на область памяти, которую необходимо освободить. Она обязана освободить эту память. Для перегрузки операторов new и delete применительно к массивам надо использовать следующий формат:
void* operator new[](size_t размер)
{
// выполнение выделения памяти
return указатель_на_памятъ;
}
void operator delete[](void *p)
{
// освобождение памяти, на которую указывает p
}
Можно перегрузить операторы new и delete глобально или относительно класса. Для того чтобы перегрузить их по отношению к классу, просто надо сделать их функциями-членами этого класса.
Глава 7. Библиотека классов ввода/вывода С++
C++ поддерживает все функции ввода/вывода языка С и, кроме того, определяет свою собственную объектно-ориентированную систему ввода/вывода. В этой главе дается обзор библиотеки классов ввода/вывода языка C++. Также описывается, каким образом можно перегрузить операторы << и >> так, чтобы выводить собственные объекты. Система ввода/вывода C++ очень велика, и мы не можем охватить ее здесь всю целиком. Однако в этой главе будут рассмотрены наиболее важные и широко используемые функции и особенности этой системы ввода/вывода.
7.1 Почему C++ имеет свою собственную систему ввода/вывода
Система ввода/вывода языка С — одна из самых мощных и гибких среди всех языков программирования. Но несмотря на мощь функций ввода/вывода С, эта система не обеспечивает поддержки объектов, определенных пользователем. Это является одной из причин, почему в языке C++ определена своя система функций ввода/вывода. Например, если на языке С создать структуру:
struct my_struct
{
int count;
char s[80];
double balance;
};
то невозможно приспособить или расширить систему ввода/вывода языка С таким образом, чтобы она могла непосредственно осуществлять ввод/вывод переменных типа my_struct. Используя подход C++ к вводу/выводу, можно перегрузить операторы << и >> таким образом, чтобы они знали, как работать с созданными программистом классами.
Хотя системы ввода/вывода С и C++ содержат фактически одинаковые операторы, тот факт, что система C++ может работать с определенными пользователем типами, неизмеримо повышает гибкость ввода/вывода. Такой подход также помогает предотвратить возникновение ошибок. Например, в следующем вызове функции scanf():
char str[80];
int i;
scanf("%d%s", str, &i);
стоящие в списке аргументов строка и целое число поменялись местами, так что %d соответствует строке str, a %s соответствует целому числу i. Такой вызов не порождает ошибки в языке С, хотя и выдает неверный результат. Тем не менее, такой вызов функции scanf() является ошибочным. По сути, при вызове функции scanf() в С не осуществляется строгой проверки типов. В противоположность этому, в C++ операции ввода/вывода для всех встроенных типов определены относительно операторов << и >>, так что возможности возникновения подобной ошибки в нем нет. Вместо этого автоматически будет вызвана корректная операция, которая определяется типом операнда. Эти черты ввода/вывода C++ имеют место также и для определенных пользователем объектов.