Лабораторная работа №8

Тема: Множественное наследование. Порядок вызова конструкторов. Конфликт имен.

Цель:

- познакомиться с различными видами наследования;

- научиться создавать программы на С++ с использованием множественного наследования;

- уметь использовать различные виды наследования в программах на С++.

Краткая теория.

Для описания иерархии множественного наследования можно использовать прямой ациклический граф (ПАГ) (Ориентированный граф наследования без петель). Подкласс может унаследовать протокол одного или более родительских классов. При этом помимо спецификаторов public и private-производных классов, используется дополнительная опция virtual.

Первый пример Класс Derived является производным от обоих базовых классов Base1 и Base2.

class Base1 {

int id;

public:

Base1(void) { cout << “ Constructor Base1”; id=0; }

Base1(int anid) { cout << “ Constructor Base1”; id=anid; }

void assignid(int anid) { id=anid; }

int accessid(void) { return id; }

};

class Base2 {

char name[20];

public:

Base2(void) { cout << “ Constructor Base2”; strcpy(name,”void”); }

Base2(char *str) { cout << “ Constructor Base2”; strcpy(name,str); }

void assignName(char *str) { strcpy(name,str); }

char *accessName(void) { return name; }

};

class Derived: public Base1,public Base2 {

char ch;

public:

Derived(void) { cout << “ Construct Derived”; ch=‘a’; }

Derived(char c,int anid,char *str):Base1(anid),Base2(str) {

cout << “ Construct Derrived”; ch=c; }

void assign(char c) { ch=c; }

friend ostream& operator<<(osream& o,Derived& d);

};

ostream& operator<<(ostream&o,Derived& d) {

o<<“id”<<d.accessid()<<“name”<<d.accessName() <<“ ch”<<c; return o;

}

main() {

Derived object1; cout << object;

Derived object2(‘e’,26,”Robert:); cout << object2;

}

Если данные описаны как private (либо, если для доступа к ним все равно используются функции доступа), то программа будет работать, так как протокол класса Derived может получить доступ к экземплярам переменных двух базовых классов, только используя методы доступа accessid, accessName. Только при попытке обратиться к экземплярам напрямую вы потерпите неудачу из-за конфликта имен.

Рассмотрим пример, когда два базовых класса содержат поля с одинаковыми именами.

class Base1 {

protected:

int id;

public:

Base1(void) { id=0; }

Base1(int anid) { id=anid; }

};

class Base2 {

protected:

int id;

public:

Base2(void) { id=0; }

Base2(int anid) { id=anid; }

};

class Derived: public Base1,public Base2 {

char ch;

public:

Derived(void) { ch=‘a’; }

Derived(char c,int anid,int aid):Base1(anid),Base2(aid) { ch=c; }

void assign(char c) { ch=c; }

friend ostream& operator<<(osream& o,Derived& d);

};

ostream& operator<<(ostream&o,Derived& d) {

o<<“id Base1”<<d.Base1::id;

o<<“id Base2”<<d.Base2::id;

o<<“ ch”<<c; return o;

}

main() {

Derived object1; cout << object;

Derived object2(‘e’,120,-150); cout << object2;

}

Объекты класса Derived содержат по два экземпляра переменных каждый. Одна id из класса Base1, а другая - id из класса Base2. Доступ к той или иной переменной в функции-члене может быть осуществлен Base1::id или Base2::id, в дружественной функции через квалификатор d.Base1::id или d.Base2::id.

Практические задания:

Множественное наследование

1. Создать иерархию типов, описывающую операционную систему и прикладное программное обеспечение, и Unix как операционную систему и прикладное программное обеспечение. Классы должны конструкторы, включая конструктор копирования, виртуальные деструкторы, перегруженные функции вывода в поток и ввода в поток.