
- •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
9.2 Функции с двумя типами-шаблонами
Можно определить несколько типов-шаблонов данных в инструкции template, используя список с запятыми в качестве разделителя. Например, следующая программа создает функцию-шаблон, имеющую два параметра типа:
#include <iostream.h>
template <class type1, class type2> void F(type1 &x, type2 &y)
{
cout << x << ' ' << у << endl;
}
int main()
{
F(10, "hi"); F(0.23, ‘q’);
return 0;
}
В этом примере типы-шаблоны type1 и type2 заменяются компилятором на типы int, char*, double и char соответственно, причем компилятор сгенерирует два различных экземпляра функции F(). Следует иметь в виду, что когда создается функция-шаблон, то по существу этим компилятору разрешается создавать столько различных версий этой функции, сколько необходимо, чтобы обрабатывать те типы данных, которые передаются этой функции при ее вызове.
9.3 Ограничения на функции-шаблоны
Функции-шаблоны сходны с перегруженными функциями, за исключением того, что они более ограниченные. Для перегруженных функций можно выполнять различные действия в теле каждой функции. В отличие от этого, для функции-шаблона необходимо выполнять одни и те же общие действия, и только тип данных может быть различным. Если перегруженные функции выполняют различные действия, то они не могут быть заменены функцией-шаблоном.
Другим ограничением на функции-шаблоны является то, что виртуальная функция не может быть функцией-шаблоном.
9.4 Классы-шаблоны
Кроме функций-шаблонов можно также определить классы-шаблоны. Для этого следует создать класс, определяющий все алгоритмы, но фактический тип данных является параметром, определяющимся при создании класса.
Классы-шаблоны полезны тогда, когда класс содержит логику, допускающую значительные обобщения. Например, алгоритм для обработки очереди целых чисел также будет работать с очередью символов. Аналогично механизм, поддерживающий связанный список почтовых адресов, также может поддерживать связанный список сведений об автомобилях. Используя классы-шаблоны, можно создавать классы, поддерживающие очереди, связанные списки, массивы и т. д. для произвольных типов данных. Компилятор автоматически создаст корректный код, основываясь на типе данных, указанном перед компиляцией. Общая форма объявления класса-шаблона показана ниже:
template <class тип1, class тип2, ...> class имя_класса
{
// тело класса
};
Здесь тип1, тип2, и т.д. являются параметрами-типами, которые будут указаны при создании экземпляра класса.
После создания класса-шаблона можно создать конкретный экземпляр этого класса, используя следующую общую форму:
имя_класса<тип> объект;
Здесь тип является именем типа данных, с которыми будет оперировать данный класс.
функции-члены класса-шаблона являются сами по себе автоматически шаблонами. Нет необходимости особым образом указывать на то, что они являются шаблонами, с использованием ключевого слова template.
В следующей программе создается класс-шаблон stack, реализующий стандартный стек «последним вошел - первым вышел». Он может использоваться для реализации стека с произвольным типом данных. В представленном здесь примере создаются стеки символов, целых чисел и чисел с плавающей точкой.
#include <iostream.h>
const int SIZE = 100;
template<class X> class stack
{
X st[SIZE];
int top;
public:
stack() { tos = 0; cout << "Stack initialized\n"; }
~stack() { cout << "Stack Destroyed\n; }
void push(X i)
{
if(top == SIZE) { cout << "Stack is full.\n"; return; }
st[top++] = i;
}
X pop()
{
if(top == 0) { cout << "Stack underflow.\n"; return 0; }
return st[--top];
}
};
int main()
{
stack<int> a; // создание целочисленного стека
stack<double> b; // создание вещественного стека
stack<char> с; //создание символьного стека
a.push(1); a.push(2); b.push(99.3); b.push(-12.23);
cout << a.pop() << " " << a.pop() << " ";
cout << b.pop() << " " << b.pop() << "\n";
for(int i=0; i<10; i++)
с.push((char)('A' + i));
for(int i=0; i<10; i++)
cout << c.pop();
cout << "\n";
return 0;
}
Как можно видеть, объявление класса-шаблона подобно объявлению функции-шаблона. Тип-шаблон используется при объявлении класса и реализации его функций-членов. При объявлении конкретного экземпляра класса stack компилятор автоматически генерирует все функции и данные, необходимые для обработки фактических данных. В этом примере объявляются три различных типа стеков (один для целых чисел, другой для вещественных и третий для символов). Рассмотрим следующее объявление:
stack<int> a; // создание целочисленного стека
stack<double> b; // создание вещественного стека
stack<char> с; // создание символьного стека
Обратим внимание, каким образом нужный тип данных подставляется в угловые скобки. Изменяя тип данных, указываемый при создании объектов класса stack, одновременно изменяется тип данных, хранящихся в стеке. Например, можно создать другой стек, хранящий указатели на символы:
stack<char*> char_ptr_stack;
Также можно создать стек, содержащий определенный тип данных. Например, можно хранить адреса, используя структуру:
struct addr
{
char name[40];
char street[40];
char city[30];
char state[3];
char zip[12]; }
Далее можно использовать класс stack для создания стека, в котором хранятся объекты типа addr:
stack<addr> obj;