
техпрог / Comp-Sci-09
.pdf
Материалы к лекции 9
Объектно-ориентированное программирование
МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ
Прямое и косвенное наследование
Пример. Прямое и косвенное наследование базового класса
// Twoclass0.cpp :
//
#include "stdafx.h" #include <iostream> using namespace std; class A {
int a; public:
A(int x) { a = x; }
int geta() { return a; }
};
// Прямое наследование базового класса class B : public A {
int b; public:

B(int x, int |
y) : A(y) |
// передача переменной y классу A |
|
{ |
b = x; |
} |
|
int getb() { |
return b; |
} |
};
//Прямое наследование производного класса
//и косвенное наследование базового класса class C : public B {
int c; public:
C(int x, int y, int z) : B(y, z) /* передача аргументов классу B */
{ |
c = x; |
} |
/* Поскольку базовые |
классы наследуются как открытые, класс C |
имеет доступ к открытым элементам классов A и B */ void show() {
cout << geta() << ' ' << getb() << ' '; cout << c << '\n';
}
};
int _tmain(int argc, _TCHAR* argv[])
{
C ob(1, 2, 3); ob.show();
// функции geta() и getb() здесь тоже открыты cout << ob.geta() << ' ' << ob.getb() << ' ';
return 0;
}
МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ
В C++ производный класс может иметь более одного базового класса. Например, так можно создать класс для фигуры Ферзь (см. пример в конце лекции)
// Класс Ферзь - наследник Ладьи и Слона class queen: public elephant, public castle
При множественном наследовании производный класс наследует элементы данных всех базовых классов и все методы этих классов. Область памяти, которую занимает объект производного класса, представляет собой сумму пространства, занимаемого в памяти объектами базовых классов.
Правила доступа для множественного наследования те же, что и для простого наследования.

Пример. Производный класс наследует сразу два класса.
#include <iostream> using namespace std;
//Создание первого базового класса class B1 {
int a; public:
B1(int x) { a = x; }
int geta() { return a; }
};
//Создание второго базового класса class B2 {
int b; public:
B2(int x)
{b = x; }
int getb() { return b; }
};
// Прямое наследование двух базовых классов class D : public B1, public B2 {
int c; public:
//здесь переменные z и y
//напрямую передаются классам B1 и B2
D(int x, int y, int z) : B1(z), B2(y) { c = x; }

/* Поскольку базовые классы наследуются как открытые, класс D имеет доступ к открытым элементам классов B1 и B2
*/
void show() {
cout << geta() << ' ' << getb() << ' '; cout << c << '\n';
}
};
int main()
{
D ob(1, 2, 3); ob.show(); return 0;
}
Все конструкторы базового класса вызываются до вызова конструкторов производного класса. Располагаются они в том порядке, в котором базовые классы перечислены в объявлении производного класса.
Если в двух базовых классах совпадают имена элементов данных или методов, то объект производного класса содержит обе копии. C++ не имеет правил предшествования для доступа к данным и методам. Неоднозначность разрешается использованием явной квалификации.
Пример.
// TwoClass.cpp : Производный класс наследует два класса
//
#include "stdafx.h" #include <iostream> using namespace std;
//Создание первого базового класса class B1 {
int a; public:
B1(int x) { a = x; }
void f(){cout<<"\n f in B1 \n";} int geta() { return a; }
};
//Создание второго базового класса class B2 {
int b; public:
B2(int x) { b = x; }
void f(){cout<<"\n f in B2\n";} int getb() { return b; }
};
//Прямое наследование двух базовых классов class D : public B1, public B2 {
int c; public:
//здесь переменные z и y
//напрямую передаются классам B1 и B2

D(int x, int y, int z) : B1(z), B2(y)
{c = x; }
/* Поскольку базовые классы наследуются как открытые, класс D имеет доступ к открытым элементам классов B1 и B2
*/
void show() {
cout << geta() << ' ' << getb() << ' '; cout << c << '\n';
}
};
int _tmain(int argc, _TCHAR* argv[])
{
D ob(1, 2, 3); ob.show();
// ob.f(); // неоднозначность ob.B1::f();
ob.B2::f(); return 0;
}
Можно поступить иначе – создать методы в производном классе, вызывающие одноименные функции из базовых классов.
// Twoclass2.cpp :
//
#include "stdafx.h" #include <iostream> using namespace std;
//Создание первого базового класса class B1 {
int a; public:
B1(int x) { a = x; }
void f(){cout<<"\n f in B1 \n";} int geta() { return a; }
};
//Создание второго базового класса class B2 {
int b; public:
B2(int x) { b = x; }
void f(){cout<<"\n f in B2\n";} int getb() { return b; }
};
//Прямое наследование двух базовых классов class D : public B1, public B2 {
int c; public:
//здесь переменные z и y
//напрямую передаются классам B1 и B2

D(int x, int y, int z) : B1(z), B2(y)
{c = x; }
/* Поскольку базовые классы наследуются как открытые, класс D имеет доступ к открытым элементам классов B1 и B2
*/
void show() {
cout << geta() << ' ' << getb() << ' '; cout << c << '\n';
}
void fb1(){B1::f();} void fb2(){B2::f();}
};
int _tmain(int argc, _TCHAR* argv[])
{
D ob(1, 2, 3); ob.show();
// ob.f(); // неоднозначность ob.fb1();// вместо ob.B1::f(); ob.fb2();//вместо ob.B2::f();
return 0;
}
Иерархия классов при множественном наследовании представляет собой граф, в простом наследовании – дерево.
Пример. В этой программе, чтобы избежать появления в классе derived3 двух копий класса base, используется виртуальный базовый класс

#include <iostream> using namespace std; class base {
public: int i;
};
// Наследование класса base как виртуального class derived1 : virtual public base { public:
int j;
};
// Здесь класс base тоже наследуется как виртуальный class derived2 : virtual public base {
public: int k;
};
/* Здесь класс derived3 наследует как класс derived1, так и класс derived2. Однако в классе derived3 создается только одна копия класса base
*/
class derived3 : public derived1, public derived2 { public:
int product() { return i * j * k; }
};
int main()
{
derived3 ob;
//Здесь нет неоднозначности, поскольку
//представлена только одна копия класса base

ob.i = 10; ob.j = 3; ob.k = 5;
cout << "Poluchili resultat = " << ob.product() << '\n'; return 0;
}
Пример. (см. Открытые системы №5-6, 2001 – www.osp.ru)
#include <iostream> #include <cmath> using namespace std;
//Использование множественного наследования для создания ферзя
//из слона и ладьи
enum coord1 {a ,b ,c, d, e, f, g, h}; // горизонталь
enum color {black, white};
//цвет фигуры
//Фигура - общий предок всех остальных class figure {
protected:
coord1 letter; // координата a..h int digit; // координата 1..8 color fig_color; // цвет фигуры
public: //конструктор
figure(coord1 x, int y, color z) : letter(x), digit(y), fig_color(z)

{} //чистая функция "ход"
virtual bool step (coord1 new_letter, int new_digit)=0;
};
//Класс Ладья реализует функцию "ход" class castle: public virtual figure {
public:
castle(coord1 x, int y, color z): figure (x, y, z){} bool step(coord1 new_letter, int new_digit)
{
if ( ((new_letter == letter)&& (new_digit != digit))|| ((new_letter != letter)&& (new_digit == digit)))
{
letter = new_letter; digit = new_digit;return true;
}
return false;
}
};
//Класс Слон реализует свою функцию "ход"
class elephant: public virtual figure
{
public:
elephant(coord1 x, int y, color z): figure (x, y, z){} bool step(coord1 new_letter, int new_digit)
{
if (abs((new_letter - letter)== abs(new_digit - digit))&& (new_letter != letter))
{ letter = new_letter; digit= new_digit; return true;} return false;
}
};
// Класс Ферзь - наследник Ладьи и Слона class queen: public elephant, public castle
{
public: //конструктор
queen(coord1 x, int y, color z): castle (x, y, z), elephant(x, y, z),
figure (x, y, z) |
{ |
} |
||
bool |
step(coord1 new_letter, int new_digit) |
|||
{ |
return castle::step(new_letter,new_digit)|| |
|||
elephant::step(new_letter,new_digit);} |
||||
}; |
|
|
|
|
void |
main(){ |
|
|
|
queen q(e, 5, white); |
|
|||
cout |
<< |
q.step(h, 8) |
<< endl; |
|
cout |
<< |
q.step(e, 8) |
<< endl; |
|
cout |
<< |
q.step(h, 8) |
<< endl; |
|
cout |
<< |
q.step(h, 5) |
<< endl; |
|
cout |
<< |
q.step(a, 8) |
<< endl; |
|
cout |
<< |
q.step(d, 8) |
<< endl; |
|
cout |
<< |
q.step(c, 5) |
<< endl; |
|
cout |
<< |
q.step(a, 1) |
<< endl; |
|
} |
|
|
|
|
