
- •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
4.5 Определение адреса перегруженной функции
Как известно, в С можно присваивать адрес функции указателю и затем вызвать функцию с использованием этого указателя. Та же самая возможность имеется и в C++. Однако из-за наличия перегрузки функций этот процесс становится несколько более сложным. Для того, чтобы понимать причину сложностей, для начала рассмотрим инструкцию, присваивающую адрес функции F() указателю p:
p = F;
Если бы это было частью С-программы, то существовала бы одна и только одна функция с именем F(), так что компилятор без труда присвоил бы ее адрес указателю р. Однако в программе на языке C++ функция F() может быть перегружена. В таком случае, каким образом компилятор может знать, какую именно функцию присваивать указателю p? Ответ зависит от того, каким образом объявлен указатель р. В качестве примера рассмотрим следующую программу:
#include <iostream.h>
int F(int a) { return a; }
int F(int a, int b) { return a*b; }
int main()
{
int (*fp)(int a); // указатель на int xxx(int)
fp = F; // указывает на F(int)
cout << fp(5);
return 0;
}
Как показывает данная программа, fp объявлен как указатель на функцию, возвращающую целое число и имеющую один аргумент. C++ использует эту информацию, чтобы выбрать версию F(int а). Если бы fp был объявлен следующим образом:
int (*fp)(int a, int b);
то указателю fp был бы присвоен адрес функции F(int a, int b). Подведем итог. Когда адрес перегруженной функции присваивается указателю на функцию, то именно способ объявления этого указателя определяет, адрес какой из перегруженных функций будет присвоен этому указателю.
4.6 Указатель this
Прежде чем перейти к перегрузке операторов, необходимо познакомиться с ключевым словом this языка C++, которое используется во многих перегруженных операторах.
Всякий раз, когда вызывается функция-член, ей автоматически передается указатель на объект, вызывающий данную функцию. Можно получить доступ к этому указателю, используя this. Указатель this служит неявным параметром всех функций-членов. (Функции-друзья не имеют указателя this.) Например, в операторе:
object.f();
функции f() автоматически передается указатель this, указывающий на объект object.
Как известно, функции-члены имеют непосредственный доступ к данным класса. Например, если задан следующий класс:
class C{
int i;
// ... };
то функция-член может присвоить переменной i значение 10, используя следующую инструкцию:
i = 10;
фактически эта инструкция является сокращенной записью следующей инструкции:
this->i = 10;
Для того чтобы увидеть, как работает указатель this, рассмотрим следующую короткую программу:
#include <iostream.h>
class C
{
int i;
public:
void load_i(int val) { this->i = val; } // то же, что и i = val
int get_i() { return this->i; } // то же, что и return i
};
int main()
{
C o;
o.load_i(100);
cout << o.get_i();
return 0;
}
Эта программа выводит на экран число 100.
Хотя рассмотренный пример достаточно тривиален — фактически никто не использует указатель this таким образом — далее будет пояснено, почему использование указателя this имеет большое значение.