
- •Введение.
- •Глава 1. Основы языка Си.
- •Алфавит.
- •1.2. Основные конструкции Си.
- •1.3.2. Ввод.
- •1.4. Базовые типы данных Языка Си.
- •1.4.1. Идентификаторы.
- •1.4.2. Типизированные константы.
- •1.4.3. Переменные.
- •1.4.3.1. Целые типы.
- •1.4.3.2. Вещественные типы.
- •1.4.3.3. Символьные типы.
- •1.4.4. Тип данных строка.
- •1.4.5. Структуры.
- •1.4.5.1. Массивы.
- •1.4.5.2. Записи.
- •1.4.6. Область видимости и время жизни переменных.
- •1.5. Основные операторы.
- •1.6. Препроцессор.
- •1.7. Программы. Функции.
- •1.8. Указатели.
- •1.9. Указатели и функции.
- •1.10. Файлы.
- •1.11. Дополнительные функции Си.
- •1.11.1. Функции преобразования.
- •1.11.2. Преобразования символьных строк: atoi(), atof().
- •1.11.3. Функции, работающие со строками.
- •1.12. Особенности программирования на языке Си.
- •1.12.1. Модули. Многомодульное программирование.
- •1.12.2. Модели памяти.
- •1.12.3 Программирование в dos и Windows
- •1.12.4 Использование языка ассемблера в программах на Си.
- •Глава 2. Примеры использования языка Си.
- •2.1. Сортировка.
- •2.2. Рекурсивные алгоритмы.
- •2.3. Задача Ханойские башни.
- •3.2.1. Классы.
- •3.2.2. Перегрузка функций.
- •3.2.3. Конструкторы.
- •3.2.4. Деструкторы.
- •3.2.5. Конструкторы с параметрами.
- •3.2.6. Введение в наследование.
- •3.2.7.Виртуальные функции.
- •3.2.8. Указатели на объекты.
- •4.2. Компоненты vcl. Свойства, события, методы.
- •В отличие от свойств методы не видны в окне инспектора объектов.
- •4.2.1.Типы компонент
- •4.2.2. Иерархия классов vcl
- •4.4.Визуальные компоненты (vcl).
- •4.5. Программы, управление событиями, исключения.
- •4.6.Стратегия отладки Windows-программ.
- •4.7.Использование компонент vcl в разработке программ.
- •4.8.Графические компоненты.
- •4.9.Мультимедиа.
- •4.10.Спрайтовая графика.
- •4.11. Объектная технология ole2.
- •4.12.Библиотеки dll.
- •4.13.Разработка визуальных компонент.
- •4.14. Введение в программирование cgi.
- •4.15. Программирование баз данных.
- •4.16. Пакеты.
- •Заключение.
- •Литература:
- •Глава 1. Основы языка Си. 4с.
- •Глава 2. Примеры использования языка Си. 48c.
3.2.7.Виртуальные функции.
Кратко проблема может быть сформулирована следующим образом: как будет вызываться функция производного класса, имеющая такое же название, что функция базового класса. Рассмотрим следующий пример.
#include <stdio.h>
class base {
public:
int i;
base(int x); //конструктор
void func()
{
printf("Базовая функция %d",i);
return;
};
};
//текст конструктора
base::base(int x)
{
i=x;
return;
};
class der1: public base {
public:
der1(int x) :base(x) {}; //конструктор
void func()
{
printf("Функция из производного класса %d", i*i);
return;
}
};
main()
{
base * pc; //указатель на базовый класс
base ob(2); //создать экземпляр объекта базового класса
der1 ob1(2); //создать экземпляр объекта производного класса
pc=&ob; //указатель на объект базового класса
pc->func(); //вызов функции базового класса
pc=&ob1; //указатель на объект производного класса
pc->func(); //попытка вызова функции производного класса
return 0;
}
На первый взгляд кажется, что в перврм случае будет вызываться функция базового класса, а во втором функция производного. Однако при проверке Вы легко убедитесь, что и в том и в другом случае будет вызвана функция функция базового класса. В чем тут дело? Дело в том, что компилятору трудно понять, какую реально функцию мы имеем в виду и он на стадии компилирования подставляет во всех тех случаях, где встречается имя func() адрес функции базового класса. Такой процесс установки адресов называется "ранним связыванием". Иногда употребляется термин "статическое связывание". Если же мы хотим, чтобы во втором случае, т.е. когда указатель pc указывал на производный класс вызывалась функция этого класса, ее еще в базовом классе следует указать как виртуальную. В нашем случае вместо строки void func() следует написать virtual void func(). После этого наш пример будет работать как надо.
Как видите ситуация несколко напоминает проблему перегрузки. Однако перегружаемые функции отличаются друг от друга типом или аргументами, здесь же функции должны быть идентичны.
В случае использования виртуальных функций адрес вызываемой функции будет определяься в процессе выполнения кода программы. Такой процесс называется "поздним связыванием", употребляется также термин "динамическое связывание".
Для дальнейшего уяснения свойств виртуальных функций рассмотрим еще один пример, являющийся развитием первого.
#include <stdio.h>
class base {
public:
int i;
base(int x); //конструктор
virtual void func()
{
printf("Базовая функция %d\n",i);
return;
};
};
//текст конструктора
base::base(int x)
{
i=x;
return;
};
class der1: public base {
public:
der1(int x) :base(x) {}; //конструктор
void func()
{
printf("Функция из производного класса %d\n", i*i);
return;
}
};
class der2: public base {
public:
der2(int x) :base(x) {}; //конструктор
};
main()
{
base * pc; //указатель на базовый класс
base ob(2); //создать экземпляр объекта базового класса
der1 ob1(2); //создать экземпляр объекта производного класса 1
der2 ob2(2); //создать экземпляр объекта производного класса 2
pc=&ob; //указатель на объект базового класса
pc->func(); //вызов функции базового класса
pc=&ob1; //указатель на объект производного класса 1
pc->func(); //попытка вызова функции производного класса
pc=&ob2; //указатель на объект производного класса 2
pc->func(); //попытка вызова функции производного класса
return 0;
}
Как видите мы ввели еще один производный класс. В нем функция func() не определена. В этом случае будет вызываться функция класса родителя. Т.е. появится строка: Базовая функция 2. Как видите принцип очень прост: если Вы хотите, чтобы вызывалась функция родительского класса, не определяйте ее в производном. Еще один вопрос может возникнуть в связи с данным примером: как быть, если мы хотим, чтобы для класса объектов der2 вызывалась функция класса der1. Решение очень просто - сделайте класс der2 наследником не класса base, а класса der1.
И последнее. Как мы видели, в производных классах функция, определенная в базовом классе как виртуальная может определяться, а может и нет. Если Вы хотите, чтобы во всех производных классах обязательно была определена виртуальная функция, то в базовом классе ее надо определить следующим образом:
virtual void func() = 0;
В этом случае базовый класс называется агрегатным и от него нельзя будет создавать экземпляры объектов, зато во всех производных классах компилятор обяжет Вас определить данную виртуальную функцию и, тем самым, уменьшить вероятность ошибок.