- •Предисловие
- •Введение
- •Введение в программирование
- •1.1. Предисловие к курсу
- •1.2. Идеология языка
- •1.3. Обзор среды Microsoft Developer Studio
- •1.4. Жизненный цикл программного обеспечения
- •1.5. Общая структура программы
- •1.6. Директивы препроцессора
- •1.7. Построение исполняемого файла
- •1.8. Строительные блоки программы
- •Контрольные вопросы
- •Типы данных. Переменные. Массивы. Операции и Указатели
- •Стандартные типы и размеры данных
- •2.1.1. Объявление переменных
- •Управляющие символьные константы
- •2.2. Объявление указателя
- •2.2.1. Операции разыменования и взятия адреса
- •2.2.2. Указатели на указатели
- •2.2.3. Арифметические операции с указателями
- •2.3. Массивы
- •2.3.1. Инициализация массивов
- •2.3.2 Динамические массивы
- •2.3.3. Методы доступа к элементам массивов
- •2.3.4. Массивы указателей
- •2.4. Строки
- •2.5. Операции
- •2.5.1. Арифметические операции
- •Арифметические операции
- •2.5.2 Операции сравнения и логические операции
- •Операции сравнения и логические операции
- •2.5.3. Побитовые операции
- •Побитовые операции
- •Контрольные вопросы
- •3.1. Базовые операторы
- •3.1.1. Оператор выражение
- •3.2.2. Оператор switch
- •3.3.4. Оператор goto
- •3.4. Операторы цикла
- •3.4.1. Оператор for
- •3.4.2. Оператор while
- •3.4.3. Оператор do..While
- •Контрольные вопросы
- •Стандартный ввод/вывод. Работа с файлами.
- •4.1. Роль стандартного ввода/вывода
- •4.1.1. Основные функции стандартного ввода/вывода
- •4.2. Понятие файла
- •4.2.1. Строение файлов
- •4.2.2. Порядок работы с файлом
- •4.2.3. Обзор библиотечных функций с для работы с файлами
- •4.3. Программные конструкции при работе с файлами
- •4.3.1. Открытие/закрытие файла
- •4.3.2. Цикл посимвольного чтения содержимого файла
- •4.3.3. Цикл построчного чтения содержимого файла
- •Контрольные вопросы
- •Функция. Пользовательские типы данных.
- •5.1. Понятие функции
- •5.1.1. Определение функции
- •5.1.2. Формальные параметры
- •5.1.3. Тип возвращаемого значения
- •5.1.4. Тело функции
- •5.1.5. Фактические параметры
- •5.1.6. Рекурсивные вызовы
- •5.1.7. Передача параметров
- •5.1.8. Библиотеки стандартных функций
- •5.2. Пользовательские типы данных.
- •5.2.1. Ключевое слово typedef
- •5.2.2. Перечислимый тип данных
- •5.2.3. Понятие структуры
- •5.2.4. Указатели на структурный объект
- •Контрольные вопросы
- •Работа с динамической памятью. Динамические структуры данных
- •6.1. Работа с динамической памятью
- •6.1.1. Статическое и динамическое распределение памяти
- •6.1.2. Основные принципы динамического распределения
- •6.1.3. Способы работы с динамической памятью
- •6.2. Динамические структуры данных
- •6.2.1. Стек
- •6.2.2.Линейный список
- •Контрольные вопросы
- •Объектно-ориентированное программирование
- •7.1. Критерии качества декомпозиции проекта
- •7.2. Новые концепции программирования
- •7.3. Достоинства ооп
- •7.4. Объекты и классы в ооп
- •7.4.1. Определение класса
- •7.4.2. Использование класса
- •7.4.3. Вложенные классы
- •Контрольные вопросы
- •Конструкторы и Перегрузка операций.
- •8.1. Перегрузка операций
- •8.1.1. Перегрузка операций внешними функциями
- •8.1.2. Перегрузка операций методами класса
- •8.2. Конструкторы и деструктор
- •8.2.1. Конструкторы и параметры
- •Контрольные вопросы
- •9.1. Простое открытое наследование
- •9.1.1 Конструкторы и деструкторы при наследовании
- •9.1.2. Поля и методы при наследовании
- •9.1.3. Вложенные классы и наследование
- •9.1.4. Закрытое наследование
- •9.1.5. Виртуальные функции
- •9.1.6. Чистые виртуальные функции и абстрактные классы
- •9.3. Основы программирования под Windows
- •9.3.1. Типы данных в Windows
- •9.4. Cреда Microsoft Developer Studio
- •9.4.1. Библиотека mfc
- •9.4.2. Архитектура приложения
- •9.4.3. Каркас приложения
- •9.4.4. Проект приложения
- •Контрольные вопросы
- •Заключение
- •Список Литературы
9.1.5. Виртуальные функции
Связывание – это сопоставление вызова функции с телом. Для обычных методов связывание выполняется на этапе трансляции до запуска программы. Такое связывание называют «ранним», или статическим. При наследовании обычного метода его поведение не меняется в наследнике. Однако бывает необходимо, чтобы поведение некоторых методов базового класса и классов-наследников отличались. Чтобы добиться разного поведения в зависимости от типа, необходимо объявить функцию-метод виртуальной; в C++ это делается с помощью ключевого слова virtual. Виртуальные функции в совокупности с принципом подстановки обеспечивают механизм «позднего» (отложенного) или динамического связывания, которое работает во время выполнения программы. Далее представлен текст программы, использующий виртуальные методы.
class Base // базовый класс
{ public:
virtual void print() const // базовый метод
{ cout << "Base!" << endl; }
class Derive: public Base // производный класс
{ public:
virtual void print() const // переопределенный метод
{ cout << "Derive!" << endl; }
};
void F(Base &d)
{ d.print(); } // предполагается вызов метода базового класса
Base W; // объект базового класса
F(W); // работает метод базового класса
Derive U; // объект производного класса
F(U); // работает метод производного класса
Base *c1 = &W; // адрес объекта базового класса
cl->print(); // вызов базового метода
cl = &U; // адрес объекта производного типа вместо базового
cl->print(): // вызывается производный метод
Класс, в котором определены виртуальные функции (хотя бы одна), называется полиморфным классом.
Ключевое слово virtual можно писать только в базовом классе – это достаточно сделать в объявлении функции. Даже если определение писать без слова virtual, функция все равно будет считаться виртуальной. Правила описания и использования виртуальных функций-методов следующие.
1. Виртуальная функция может быть только методом класса.
2. Любую перегружаемую операцию-метод класса можно сделать виртуальной, например операцию присваивания или операцию преобразования типа.
3. Виртуальная функция наследуется.
4. Виртуальная функция может быть константной.
5. Если в базовом классе определена виртуальная функция, то метод производного класса с таким же именем и прототипом (включая и тип возвращаемого значения, и константность метода) автоматически является виртуальным (слово virtual указывать необязательно) и замещает функцию-метод базового класса.
6. Статические методы не могут быть виртуальными.
7. Конструкторы не могут быть виртуальными.
8. Деструкторы могут (чаще – должны) быть виртуальными – это гарантирует корректное освобождение памяти через указатель базового класса.
Внутри конструкторов и деструкторов динамическое связывание не работает, хотя вызов виртуальных функций не запрещен. В конструкторах и деструкторах всегда вызывается «родная» функция класса.
Виртуальные функции-методы можно перегружать и переопределять (в наследниках) с другим списком аргументов. Если виртуальная функция переопределена с другим списком аргументов, она замещает (скрывает) родительские методы. Константный метод считается отличным от неконстантного метода с таким же прототипом. Текст программы перегрузки и переопределения виртуальных методов.
class Base
{ public: // перегрузка виртуальных методов
virtual int f() const { cout << "Base::f()"<< endl; return 0; }
virtual void f(const string &s) const
{ cout « "Base: :f(string)"« endl: }
};
class Derive: public Base
{ public:
virtual int f(int) const // переопределение виртуальной функции
{ cout << "Derive::f(int)"<< endl; return 0: }
};
Base b. *pb; // объекты базового типа
Derive d. *pd = &d; // объекты производного типа
pb = &d; // здесь нужна виртуальность
pb->f(); // вызывается базовый метод
pb->f("name"); // вызывается базовый метод
pb->f(l); // ошибка!
pd->f(l); // нормально - вызов метода наследника
Через указатель базового класса нельзя вызвать новые виртуальные методы, определенные только в производном классе. Если это необходимо, требуется явное приведение типа:
((Derive *)pb)->f(l);
Родительские методы можно сделать доступными в классе-наследнике при помощи using-объявления.
class Derive: public Base
{ public:
virtual int f(int) const
{ cout << "Derived::f(int)"<< endl: return 0: }
using Base::f; // разрешение использовать скрытые базовые методы
};
Разрешается при переопределении виртуальной функции изменить только тип возвращаемого значения, если это указатель или ссылка.
Виртуальную функцию можно вызвать невиртуально, если указать квалификатор класса:
Base *cl = new Derive(); // адресуется объект производного класса
cl->print(); // вызов метода-наследника
cl->Base::print(); // явно вызывается базовый метод
Такой статический вызов бывает нужен, когда в базовом классе реализуются общие действия, которые должны выполняться во всех классах-наследниках.
При наличии хотя бы одной виртуальной функции размер класса без полей равен 4 – это размер указателя. Количество виртуальных функций роли не играет – размер класса остается равен 4.
class One
{ virtual void f(void) {}
};
class Two
{ virtual void f(void) {}
virtual void g(void) {}
};
// ...
cout << sizeof(One) << endl; // размер = 4
cout << sizeof(Two) << endl; // размер = 4