Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие-ООП - копия.doc
Скачиваний:
20
Добавлен:
17.08.2019
Размер:
907.78 Кб
Скачать

Виртуальные базовые классы

Виртуальным базовым классом называется общий базовый класс для нескольких производных классов, производные классы от которых наследуют только одну копию общего базового класса.

Виртуальный базовый класс имеет следующие свойства:

  • объявляется добавлением ключевого слова virtual к атрибуту наследования базовых классов, производным от общего виртуального базового класса; Пример для предыдущей схемы:

class cl1 {...}//виртуальный базовый класс

class cl2:virtual public cl1 {...}

class cl3:virtual public cl1 {...}

class cl4:public cl2,public cl3 {...}

  • наследуется только одна копия виртуального базового класса в классах, порожденных от производных классов с общим виртуальным базовым классом; точнее, имеются скрытые указатели на одну унаследованную часть общего виртуального базового класса, например, объект obj4 класса cl4 имеет в памяти следующие части:

указатель-----------------------------------------> элементы,

элементы, унаследованные явно от cl2; унаследованные

указатель------------------------------------------> от cl1

элементы, унаследованные явно от cl3;

собственные элементы класса cl4;

- сохраняются все правила доступа к наследуемым и собственным элементам производного класса, но устранена неоднозначность при доступе к унаследованным элементам виртуального базового класса (доступ прямо по имени элемента), т.к. имеется только одна копия виртуального базового класса; например, доступ к элементу tabn класса cl1 возможен с помощью выражения obj4.tabn;

- устранена неоднозначность доступа при совпадении имен элементов в виртуальном базовом классе и производных от него, т.к. действует приоритет самого дальнего имени от вершины иерархии; например, при наличии элемента-функции vvod() в классах cl1 и cl2 оператор obj4.vvod() вызовет элемент-функцию vvod() класса cl2;

- конструктор виртуального базового класса вызывается не конструктором ближайшего производного класса, как обычно, а конструктором последнего производного класса в иерархии классов, в списке инициализации которого задается прототип конструктора виртуального базового класса; порядок выполнения конструкторов изменяется: сначала выполняется конструктор виртуального базового класса, затем конструкторы не виртуальных базовых классов, затем конструктор производного класса; например, конструктор класса cl4 может иметь вид: cl4(): cl1(),cl2(),cl3() {...}

2.3Полиморфизм. Лекция №10

Общий полиморфизм имеет следующие методы:

  • перегрузка операций;

  • преобразования, определяемые классом;

  • перегрузка функций.

Перегрузка операций

В C++ операции определены для встроенных типов данных. Перегрузка операций - это переопределение действий операций применительно к объектам конкретных классов.

Средствами перегрузки операций являются специальные функции-операции с ключевым словом operator. Синтаксис функции:

tip operator @(t1 per1[,...])//@ - перегружаемая операция

{...} //tip - тип возвращаемого значения

//t1 per1[,...]- параметры функции

Функция-операция характеризуется следующими свойствами:

  • перегружает все операции, кроме (.),(.*),(::),(?:); операция присваивания (=) уже предопределена для любого типа;

  • наследуется, кроме функции-операции operator=();

  • должна быть либо элементом-функцией класса, либо внешней функцией, но дружественной данному классу;

если функция-операция является внешней и дружественной данному классу, то для бинарных операций она должна иметь два параметра, для унарных - один параметр; выражение obj1@obj2 интерпретируется как operator @(obj1,obj2), выражение @obj интерпретируется как operator @(obj);

если функция-операция является элементом-функцией данного класса, то тогда ей передается неявный указатель this на текущий объект класса, т.е. она уже имеет один неявный параметр, и именно первый; поэтому, для бинарных операций она должна иметь один параметр, для унарных - вообще без параметров; выражение obj1@obj2 интерпретируется как obj1.operator@(obj2), т.е. результат заносится в obj1, а выражение @obj интерпретируется как obj.operator@(), т.е. результат заносится в obj;

  • обычно использует в качестве параметров ссылки на объекты, а в качестве возвращаемого значения - значение объекта;

  • критерия выбора между элементом класса и внешней функцией не существует, т.к. это дело вкуса; но можно предложить некоторые рекомендации: если функция-операция должна изменять значения элементов класса, то лучше ее определить как элемент класса (например, операции ++,--,+=,*= и т.д.); для бинарных операций лучше ее определить как внешнюю функцию, т.к. это естественней.

Примеры перегрузки (+) для сложения комплексных чисел.

//Пример 1: функция-операция + , как внешняя функция

//файл заголовков complex1.hpp

#include <iostream.h>

#include <conio.h>

class complex

{

public:

float d,m;

complex() {d=m=0;}

complex(float x,float y) {d=x;m=y;}

friend complex operator +(complex& x1,complex& x2);

};

//файл кодов complex1.cpp

#include "complex1.hpp"

complex operator +(complex& x1,complex& x2)

{

complex y;

y.d=x1.d+x2.d;

y.m=x1.m+x2.m;

return(y);

}

main()

{

clrscr();

complex obj1(0,1),obj2(1,0),obj3;

cout<<obj1.d<<"+i*"<<obj1.m<<'\n'; //результат: 0+i*1

obj3=obj1+obj2; //интерпрет.,как obj3=operator+(obj1,obj2);

cout<<obj1.d<<"+i*"<<obj1.m<<'\n'; //результат: 0+i*1

cout<<obj2.d<<"+i*"<<obj2.m<<'\n'; //результат: 1+i*0

cout<<obj3.d<<"+i*"<<obj3.m<<'\n'; //результат: 1+i*1

getch();

}

Пример 2: функция-операция +, как элемент класса

//файл заголовков complex2.hpp, заменим прототип друж. функ.

#include <iostream.h>

#include <conio.h>

class complex

{//... см. файл complex1.hpp

complex operator +(complex& x); };

//файл кодов complex2.cpp

#include "complex2.hpp"

complex complex::operator +(complex& x)

{

complex y;

y.d=d+x.d;

y.m=m+x.m;

return(y); //main без изменений (см. complex1.cpp), результаты те же

}//теперь obj3=obj1+obj2; интерпретир. как obj3=obj1.operator +(obj2);

Пример полезных перегруженных операций извлечения (>>) и вставки (<<) для ввода/вывода объектов класса, и перегруженной операции отрицания (!) для проверки наличия данных объекта:

//Пример: перегрузка операций (>>), (<<), (!)

//файл кодов peregruz.cpp

#include <iostream.h>

#include <conio.h>

class akt

{

public:

char tabn[10];

float objem;

akt() { tabn[0]=’\0’; }

int operator!(); //Элемент-функция класса

friend istream& operator>>(istream& is, akt& z); //внешняя друж. функция

friend ostream& operator<<(ostream& os, akt& z);//внешн. друж. функция

};

int akt::operator!() //перегрузка (!) для проверки наличия данных объекта

{ return(tabn[0]==’\0’; }

istream& operator>>(istream& is, akt& z) //перегрузка (>>) для объектов

{ //класса akt

cout<<”Таб. номер :”;

is>>z.tabn;

cout<<”Объем работ:”;

is>>z.objem;

return(is);

}

ostream& operator<<(ostream& os, akt& z)

{

os<<”Таб. номер:”<<z.tabn<<’\n’;

os<<”Объем :”<<z.objem<<’\n’;

return(os);

}

main()

{ clrscr();

akt obj;

if (!obj) cout<<”Объект пустой, введите данные:\n”;

cin>>obj;

if (!!obj) cout<<”Объект имеет данные:\n”;

cout<<obj;

getch();

}