Производный класс может иметь любое число базовых классов. Использование двух или более классов называется множественным наследованием.
При инициализации объекта производного класса сначала вызываются конструкторы базовых классов, в порядке их перечисления в объявлении производного класса, а потом – конструктор производного класса.
Пример. Пусть INTEGER – класс, объектами которого являются целые числа, POSITIVE – класс положительных целых чисел. Определим рациональную дробь как объект производного класса от этих двух классов. Пара, представляющая рациональную дробь, состоит из взаимно простых целых чисел.
#include <iostream.h> //библиотека потокового ввода-вывода
#include <process.h> //библиотека с прототипом функции exit
#include <conio.h> //библиотека консольного ввода-вывода
class INTEGER //класс целых чисел
{
public:
long NUM; //информационное поле
INTEGER (long Val): NUM(Val) {} //конструктор
};
class POSITIVE //класс положительных чисел
{
public:
unsigned long Den; // информационное поле
POSITIVE(unsigned long d) : Den(d) //конструктор
{
if(d==0) {cout << "Ошибка"; exit(1);}//ноль недопустим
}
};
class RATIONAL : public INTEGER, public POSITIVE
//класс дроби
{
//дружественная функция вывода дроби в некоторый поток
friend ostream &operator<<(ostream& stream, RATIONAL& o);
public:
RATIONAL(long v, unsigned long u=1): INTEGER(v), POSITIVE(u)
//конструктор
{
long w;
if (v==0) {u=1; return;}
if(v<0) {w = -v;}
else
{
w=v;
}
//поскольку числитель и знаменатель должны быть
//взаимно простыми числами то следует найти наибольший
//общий делитель для числителя и знаменателя
while (w!=u)
{
if(w>u) w=w-u;
if(u>w) u=u-w;
}
//и следует сократить дробь
NUM = NUM/w;
Den = Den/w;
}
};
ostream& operator<<(ostream& stream, RATIONAL& o)
{
stream<<o.NUM<<"/"<<o.Den;
return stream;
}
main()
{
RATIONAL r1(10, 20), r2(-15, 10);
clrscr();
cout<<"Первая дробь (числитель равен 10, знаменатель равен 20): ";
cout<<r1<<"n";
cout<<"Вторая дробь (числитель равен -15,знаменатель равен 10): ";
cout<<r2<<"n";
getch();
}
Результаты работы программы
Первая дробь (числитель равен 10, знаменатель равен 20): 1/2
Вторая дробь (числитель равен -15,знаменатель равен 10): -3/2
В данном примере при инициализации объекта – рационального числа – сначала будет вызван конструктор INTEGER, затем – конструктор класса POSITIVE, затем – конструктор класса RATIONAL.
Доступ к членам базовых классов, имеющих одинаковые имена, осуществляется через имена базовых классов, которым они принадлежат, при помощи операции разрешения доступа. Например:
Class A
{
public: void f();
};
class B
{
public: void f();
};
class C : public A, public B {};
main()
{
C c;
c.f();
// ошибка – неизвестно, какая из функций вызывается A::f() или B::f()
c.A::f(); // правильный вызов
}
На рис. 4.1 приведена иерархическая структура, иллюстрирующая множественное наследование из приведённого выше примера.
Неоднозначность, возникающая при вызове функций, может быть преодолена с помощью переопределения функции в производном классе, например:
Class C : public A, public B
{
public: void f() { A::f();}
}
int main()
{
C c;
c.f(); // правильный вызов функции A::f()
c.B::f(); // правильный вызов функции B::f()
}
Базовые классы с одинаковым именем не могут присутствовать в определении производного класса. Например, если попытаться определить вектор, как пару точек
Class Point {int x,y;}
Class Vector : public Point, public Point {} // ошибка
то компилятор выведет сообщение об ошибке, поскольку для объекта
Vector v;
неясно, как начальную точку вектора отличить от конечной.
Для классов, порожденных от производных классов с общей базой, по умолчанию существует два экземпляра объекта общей базы. Это позволяет наследовать базовый класс любое количество раз, например, определение:
Class Point {public: int x, y;}
Class Point2 : public Point {};
Class Vector : public Point, public Point2 {};
будет верным и будет задавать вектор как пару точек. Теперь обращение к начальной и конечной точкам вектора будет производиться с помощью оператора разрешения области видимости. Для данного случая иерархическая структура классов приведена на рис. 4.2.