virtual void F1(); // Виртуальные функции
virtual void F2(), F3();
virtual A* Pf(); // Возвращает указатель на базовый класс.
};
class D : public C // Производный класс
{
public:
void F1(); // Виртуальная F1()
void F2(int); // Невиртуальная, т.к. другой тип аргумента. Она скрывает C::F2()
int F3(); // Ошибка. Возвращает не тот тип
void f(); // Невиртуальная. Скрывает C::f()
B* Pf(); // Ошибка. Различие в типе возвращаемого значения
A* Pf(); // Виртуальная Pf()
};
void main()
{
D d; // Объект производного класса
C *pc=&d; // Инициализация указателя на базовый класс адресом объекта производного класса
pc->F1(); // Вызов D::F1(), т.к. F1() виртуальная
pc->F2(); // Вызов C::F2(), т.к. D::F2() невиртуальная
pc->F2(1); // Ошибка, т.к. C::F2() без аргументов
pc->f(); // Вызов C::f(), т.к. f() невиртуальная
A* pa=pc->Pf();// Вызов D::Pf(), т.к. Pf() виртуальная. Преобразование возвращенного значения из B* в A*.
// Это исключение из правила
D* pd=&d; // Полиморфизм раннего связывания
B* pb=(B*)pd->Pf(); // Вызов D::Pf(). Преобразование результата
}
Дополните коды последнего примера так, чтобы все вызовы функций выводили сообщение: class C или class D в зависимости от принадлежности вызываемой функции тому или иному классу. Для минимизации исправлений используйте макроподстановку:
#define P(x) printf("\n class %c",x)
Виртуальные функции полезны в случаях, когда используются динамические структуры данных вида список, очередь или дерево для манипуляции объектами разных классов. Предположим, что перед нами стоит задача создать динамический линейный список, в котором хранятся объекты с несколько отличным набором полей. Для определенности пусть это будет список людей (объектов классов производных от класса Man). Для пояснения принципа функционирования такого гетерогенного (неоднородного) списка достаточно взять два типа людей: студент и профессор. Абстракция понятия Человек логически приводит к целесообразности инкапсуляции в базовом классе общих для всех людей свойств. Минимальный набор общих полей данных может содержать имя и возраст. Чтобы задача не стала громоздкой будем и далее выбирать минимум данных, необходимых для иллюстрации положений ООП.
Определим базовый класс Man, содержащий общие для всех людей данные. Для каждого из конкретных типов людей определим подклассы, например: Stud и Prof. Каждый подкласс, кроме наследованных из базового класса данных, должен содержать свои собственные поля, выделяющие его из абстрактного типа данных Человек. Пусть ими будут: номер курса для студента и количество статей и кафедра для профессора. Важно то, что типы полей различны в подклассах, а набор методов одинаков.
В классе Man объявим чисто виртуальную функцию с использованием синтаксиса:
virtual string Class ()=0;
Таким образом класс автоматически переводится в разряд абстрактных. Это в свою очередь означает, что объекты класса Man не могут существовать. Они не могут быть в качестве типа возвращаемого функцией значения и в качестве параметра функции. Однако, указатель или ссылка на объект абстрактного класса может быть создана. Они должны указывать (или ссылаться) на объекты производного класса.
Если в абстрактном классе объявлена чисто виртуальная функция и она не переопределена (overriden) в производном классе, то этот производный класс остается абстрактным, так как он наследовал чисто виртуальную функцию и не переопределил ее. Абстрактные классы используются только для того, чтобы от них могли быть образованы производные классы. Это — своеобразный генетический материал для будущих классов. Обычно они стоят во главе иерархического дерева классов. Конструктор абстрактного класса служит не для создания объектов своего класса, а для инициализации данных производных (не абстрактных классов). Точнее, именно тех полей производного класса, которые перешли по наследству от абстрактного. Методы абстрактного класса могут быть вызваны в теле конструктора, исключая, конечно, чисто виртуальные функции.
Создайте проект SPList консольного типа. Он состоит из двух файлов: файла предварительно компилируемых заголовков (stdafx.h) и основного файла с кодом (SPList.cpp). Содержимое файла "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
Содержимое файла "SPList.cpp"
#include "stdafx.h"
//============== Global types and variables ===================//
enum eSortBy { None, ByName, ByAge, ByStatus }; // Возможные режимы сортировки списка
const int gNameSize = 15, gDeptSize = 20, gBufSize = 256;
char buf[gBufSize];
//===================== Global funcs ======================//
void Clear (istream& is) //------- Clear keyboard buffer
{
if (cin.eof())
return;
int n = is.rdbuf()->in_avail(); // How many bytes left?
while (n--) // Eat out all of them
is.rdbuf()->sbumpc();
is.clear(); // Clear the failbit
}
int In (string s, int min, int max) //------- Safe Input
{
cout <<s<<": ";
int n=0;
while (!(cin >> n) || n < min || max < n)
{
Clear(cin);
cout <<"\nOut of range: ("<<min<<", "<<max<<")\n"
<< "\nEnter "<<s<<": ";
}
Clear(cin);
return n;
}
char Menu()//------- Main Menu
{
cout << "\n\n\t\tq - Quit"
"\n\t\ts - Show"
"\n\t\ta - Add"
"\n\t\td - Delete"
"\n\t\tr - Read"
"\n\t\tw - Write"
"\n\t\tt - Sort\n\n";
char c;
cin >> c; Clear(cin);
return tolower(c);
}
char SortMenu()
{
cout << "\n\n\t\tSorting\n"
"\n\t\tn - By Name"
"\n\t\ta - By Age"
"\n\t\ts - By Status"
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.