Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Абстрактные классы и тп.doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
304 Кб
Скачать
☆

Чистые виртуальные деструкторы

В отличие от чистых виртуальных функций для чистых виртуальных деструкторов необходимо обеспечить тело функции. При реализации деструкторов производных классов их тело задавать не нужно.

//f1.h

#include <iostream.h>

class AbstractBase

{public:

virtual ~AbstractBase()=0;

};

AbstractBase::~AbstractBase()

{cout<<"Destructor Abstract Class"<<endl;}

class Derived: public AbstractBase

{

};

//f1.cpp

#include "f1.h"

void main()

{ Derived d;

}

Виртуальные функции в деструкторах

Механизм позднего связывания не действителен для виртуальных и невиртуальных деструкторов. Вызывается локальная версия виртуальной функции.

//f1.h

#include <iostream.h>

class Base

{public:

virtual ~Base() {cout<<"~Base()"<<endl; f();}

virtual void f() {cout<<"Base::f"<<endl;}

};

class Derived: public Base

{public:

~Derived() {cout<<"~Derived()"<<endl;}

virtual void f() {cout<<"Derived::f"<<endl;}

};

//f1.cpp

#include "f1.h"

void main()

{ Base *bp = new Derived;

delete bp; //"~Derived()","~Base()","Base::f"

}

Перегруженные функции при динамическом связывании

Общее правило перегрузки функций: перегруженная (переопределенная) функция базового класса в производных классах прячет все другие версии этой функции в базовом классе.

//f1.h

#include <iostream.h>

class Base

{public:

virtual int f() {cout<<"Base::f()"<<endl;return 1;}

virtual void f(char *s) {cout<<s<<endl;}

virtual void g() {};

};

class Derived1: public Base

{public:

void g() {cout<<"Derived1::g()"<<endl;}

};

class Derived2: public Base

{public:

int f() {cout<<"Derived2::f()"<<endl;return 2;}

};

/*class Derived3: public Base

{public:

void f() {cout<<"Derived3::f()"<<endl;} //Ошибка, так как компилятор не позволяет изменять тип возвращаемого значения для перегруженной виртуальной функции

};*/

//f1.cpp

#include "f1.h"

void main()

{ char *s;

s="Hello!";

Derived1 d1;

int x=d1.f(); //Base::f()

d1.f(s); //"Helo!"

Derived2 d2;

x=d2.f(); //Direved2:f()

//d2.f(s); //ошибка! функция f не имеет ни одного параметра

Base* b2=&d2;

b2->f();//Direved2:f()

b2->f(s);//Hello!

}

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

Множественное наследование описывает родство между классами, при котором один класс может иметь несколько базовых.

class A {…};

class B {…};

class C {…};

class D: public A, public B, public C {…};

Вызов конструкторов происходит в том порядке, в котором были объявлены базовые классы:

A::A()

B::B()

C::C()

D::D()

//f1.h

class A

{public:

void Display(void) {…}

};

class B

{public:

void Display(void) {…}

};

class C: public A, public B

{public:

void f(void);

};

Если имеем реализацию:

void C::f(void)

{Display();}

то компилятор не будет знать, какую функцию Display() вызывать. Нужно указывать явно.

void C::f(void)

{ A::Display();

B::Display();

}

Конструктор класса, у которого несколько родителей, перед вызовом своего конструктора вызывает все конструкторы базовых классов в порядке их объявления. Их можно вызвать явно:

class C: public A, public B

{public:

C():A(), B() {};

void f(void);

};

Если через объект класса C попытаться обратиться к методам класса W, то компилятор выдаст ошибку. Выходом из этой проблемы будет использование виртуального базового класса

//f1.h

#include <iostream.h>

class W

{public:

virtual void f() {cout<<"W::f()"<<endl;}

virtual void g() {cout<<"W::g()"<<endl;}

virtual void h() {cout<<"W::h()"<<endl;}

};

class A:public virtual W

{public:

void g() {cout<<"A::g()"<<endl;}

};

class B:public virtual W

{public:

void f() {cout<<"B::F()"<<endl;}

};

class C: public A, public B

{public:

void f() {cout<<"C::f()"<<endl;}

};

//f1.cpp

#include "f1.h"

void main()

{ C* pc = new C;

pc->f(); //C::f()

pc->g(); //A::g()

pc->h(); //W::h()

((A*)pc)->f(); //C::f(). pc – указатель на C, насильственно преобразуем его к указателю на A; в описании класса A функции f нет, она описана в базовом классе W, но как виртуальная; далее происходит анализ класса, на который указывает pc, то есть класса C, в нем обнаруживается реализация функции f(), вызов которой и происходит. Если слово virtual перед f() в W убрать, тогда будет вызвана f из W.

((W*)pc)->f(); //C::f()

B* pb = new B;

pb->f(); //B::f()

pb->g(); //W::g();

pb->h(); //W::h()

((W*) pb)->f(); //B:f()

A* pa = new A;

pa->f(); //W::f()

pa->g(); //A::g()

pa->h(); //W::h()

((W*) pa)->g(); //A::g()

}

Для описания иерархий множественного наследования используется прямой ациклический граф. Виртуальные базовые классы инициализируются перед любыми невиртуальными базовыми классами в том порядке, в котором они появляются в прямом ациклическом графе наследования при просмотре его снизу вверх и слева направо. Для приведенного выше примера порядок вызова конструкторов следующий: W(), A(), B(), C().

class A {};

class B: public virtual A {};

class C: public virtual A {};

class D: public A {};

class E: public B, public C, public D {};

Дерево наследования приведенной выше иерархии изображено на рисунке ниже.

Порядок вызова конструкторов для класса E таков: A(), B(), C(), D(), E().

Ключ доступа

Спецификатор базового доступа

Доступ в производном классе

Комментарий

private

private

protected

public

private

private

private

все private

protected

private

protected

public

private

protected

protected

все protected, кроме private

public

private

protected

public

private

protected

public

не изменяется

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]