Объектно-ориентированное программирование, страница 15

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"