
- •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
8.9 Собственные операторы извлечения и вставки
Поскольку основанные на массивах потоки продолжают оставаться потоками, то можно создавать собственные функции извлечения и вставки в точности тем же способом, что и для потоков других типов. Например, в следующей программе создается класс plot, поддерживающий координаты X и Y точек в двумерном пространстве. Перегруженный для этого класса оператор вставки отображает положение точки. Для простоты область изменения координат X и Y ограничена пределами от 0 до 5.
#include <strstrea.h>
#include <iostream.h>
const int size=5;
class plot
{
int x, y;
public:
plot(int i, int j)
{
if(i>size) i = size;
if(i<0) i=0;
if(j>size) j = size;
if(j<0) j=0;
x=i; y=j;
}
friend ostream& operator<<(ostream &stream, plot p);
}
ostream& operator<<(ostream &stream, plot p)
{
stream << p.x << “ “ << p.y << “\n”;
return stream;
}
int main()
{
plot a(2, 3), b(1, 1);
cout << "Output using cout:\n";
cout << a << “\n” << b << "\n\n";
char str[200];
ostrstream outs(str, sizeof(str));
outs << а << b << ends;
cout << "Output using in-RAM formatting:\n";
cout << str;
}
Эта программа выведет следующий результат:
Output using cout:
2 3
1 1
Output using in-RAM formatting:
2 3
1 1
8.10 Форматирование на основе массивов
В языке С функции sprintf() и sscanf() особенно полезны для подготовки данных к выводу или чтения входных данных с нестандартных устройств. Тем не менее, благодаря возможности языка C++, перегружая операторы вставки и извлечения для соответствующего класса и создавая собственные манипуляторы, можно достаточно легко работать со многими экзотическими устройствами. Благодаря сказанному, потребность в форматировании в памяти не так уж и велика. Тем не менее, остается много приложений для такого форматирования.
Одним из распространенных применений форматирования на основе массивов служит конструирование строки, используемой в качестве входа или функции стандартной библиотеки, или какой-то другой внешней функции. Например, может потребоваться создать строку, которая будет передана функции strtok() для синтаксического разбора. Функция strtok() разбивает строку на ее элементы. Другим применением ввода/вывода на основе массивов служат текстовые редакторы, выполняющие сложные операции форматирования. Часто проще использовать конструкции форматированного ввода/вывода C++ на основе массивов для построения сложной строки, чем использовать для этого «подручные» средства.
Глава 9. Шаблоны и библиотека stl
Используя шаблоны, можно создавать функции-шаблоны и классы-шаблоны. Для функции-шаблона или класса-шаблона тип данных, которыми оперирует функция или класс, является параметром. Таким образом, можно использовать одну функцию или класс для нескольких различных типов данных без необходимости явного написания кода для каждой версии в отдельности.
9.1 Функции-шаблоны
Функция-шаблон определяет общий набор операций, который будет применен к данным различных типов. Используя этот механизм, можно применять некоторые общие алгоритмы к широкому кругу данных. Как известно, многие алгоритмы логически одинаковы вне зависимости от типа данных, с которыми они оперируют. Например, алгоритм быстрой сортировки quicksort один и тот же и для массива целых чисел, и для массива чисел с плавающей запятой. Отличается только тип данных, подлежащих сортировке. При помощи создания функции-шаблона (generic function) можно определить сущность алгоритма безотносительно к типу данных. После этого компилятор автоматически генерирует корректный код для того типа данных, для которого создается данная конкретная реализация функции на этапе компиляции. По существу, когда создается функция-шаблон, создается функция, которая может автоматически перегружать сама себя.
Функции-шаблоны создаются с использованием ключевого слова template (шаблон). Обычное значение слова «шаблон» достаточно полно отражает его использование в C++. Шаблон используется для создания каркаса функции, оставляя компилятору реализацию подробностей. Общая форма функции-шаблона имеет следующий вид:
template<class тип1, class тип2, ...> возвращаемый_тип имя_функции(список параметров)
{
// тело функции
}
Здесь тип1, тип2, и т.д. являются параметрами-типами и представляют типы данных, которые используются функцией. Эти параметры-типы могут быть использованы в определении функции. Во время создания конкретной версии функции компилятор автоматически заменяет эти параметры на фактические типы данных.
Ниже приведен короткий пример, в котором создается функция-шаблон, имеющая два параметра. Эта функция меняет между собой величины значений этих параметров. Поскольку общий процесс обмена значениями между двумя переменными не зависит от их типа, то он естественным способом может быть реализован с помощью функции-шаблона.
#include <iostream.h>
template <class X> void swap(X &a, X &b)
{
X temp = a; a = b; b = temp;
}
int main()
{
int i=10, j=20;
float x=10.1, y=23.3;
char a='x', b='z';
cout << "Original i, j: " << i << “ “ << j << endl;
cout << "Original x, y: " << x << “ “ << у << endl;
cout << "Original a, b: " << a << “ “ << b << endl;
swap(i, j); // обмен целых
swap(x, y) ; // обмен вещественных значений
swap(a, b) ; // обмен символов
cout << "Swapped i, j: " << i << “ “ << j << endl;
cout << "Swapped x, y: " << x << “ “ << у << endl;
cout << "Swapped a, b: " << a << “ “ << b << endl;
return 0;
}
Рассмотрим эту программу более внимательно. Строка
template <class X> void swap(X &a, X &b)
указывает компилятору, что создается шаблон. Здесь X — имя параметра-типа. Далее следует объявление функции swap() с использованием типа данных X для тех параметров, которые будут обмениваться значениями. В функции main() функция swap() вызывается с передачей ей данных трех различных типов: целых чисел, чисел с плавающей запятой и символов. Поскольку функция swap() является функцией-шаблоном, то компилятор автоматически создаст три разные версии функции swap() — одну для работы с целыми числами, другую для работы с числами с плавающей запятой и, наконец, третью для работы с переменными символьного типа.