Такой способ наследования тесно связан с другой возможностью C++ - множественным наследованием. Я бы даже сказал, он вполне логично дополняет один из способов применения множественного наследования.
Рассмотрим такой пример:
Все дальнейшие манипуляции с классами будем проводить в main().
class A {
public:
int var_a;
virtual void foo() = 0;
};
class B: public A {
public:
int var_b;
void bar()
{
foo();
}
};
class C: public A {
public:
int var_c;
void foo()
{
cout << "This is C::foo()" << endl;
}
};
class D: public B, public C {
int var_d;
};
Например, объявим экземпляр класса D и попробуем присвоить что-нибудь переменной var_a:
D d;Компилятор не пропустит такое выражение и вот почему. Дело в том, что при использовании обычного невиртуального наследования экземпляр объекта D имеет следующую логическую структуру:
d.var_a = 2;
Как видно из схемы, мы имеем две переменных var_a, одна их которых принадлежит классу A, который является базовым классом для B, а другая принадлежит классу A, который является базовым классом для C.
Для обхода этой ситуации можно конечно явно указать, какую переменную мы имеем ввиду:
d.B::A::var_a = 2;или:
d.C::A::var_a = 2;Но это, согласитесь, не очень красиво, да и не всегда логически необходимо иметь два экземпляра базового класса.
Вот именно в этом случае к нам приходит на помощь виртуальное наследование.
Есть и еще один нюанс в нашем примере: взгляните на функцию B::bar(). В ней происходит вызов виртуальной функции A::foo(), которая для этой ветви иерархии неопределена - ни в классе A, ни в классе B, ни в классе D. К классу C функция B::bar() доступа не имеет, так как C по сути наследует от другого экземпляра A.
Что же будет, если применить виртуальное наследование? Слегка изменим наш пример:
class A {Теперь экземпляр класса D будет иметь другую логическую структуру:
public:
int var_a;
virtual void foo() = 0;
};
class B: public virtual A {
public:
int var_b;
void bar()
{
foo();
}
};
class C: public virtual A {
public:
int var_c;
void foo()
{
cout << "This is C::foo()" << endl;
}
};
class D: public B, public C {
int var_d;
};
Как видно из рисунка теперь для классов B и C создается единственный общий экземпляр класса A.
Таким образом код
D d;становится допустимым.
d.var_a = 2;
При применении виртуального наследования становится возможном и еще один интересный финт ушами - использование виртуальной функции, фактически определенной не в родительском и не в дочернем классах, а в классе-брате. В нашем примере - это виртуальная функция A::foo(), которая определена в классе C - C::foo(). Посмотрите, она используется из функции B::bar(), который является братом класса C.
Естественно, чтобы это работало, мы должны создать экземпляр такого объекта, который бы наследовал от обоих классов B и C, в нашем случае - это класс D. Таким образом, становится допустимым код:
D d;Программиста, который получил в свой метод ссылку типа B, вообще может ввести в заблуждение работоспособность подобного кода, ведь он не знает о том, что на самом деле эта ссылка указывает на объект типа D (он вовсе не видит объявления класса D), который наследует и от C тоже. С его точки зрения это будет казаться мистикой - ведь функция bar() вызывает виртуальную функцию foo(), которая с его точки зрения НИГДЕ не определена!
B& b = d; //создали ссылку типа B на объект типа D.
b.bar(); //вызываем функцию bar(), которая в свою очередь вызывает функцию foo(), которая фактически определена в классе C.
Ну и напоследок еще одна задачка, связанная с множественным наследованием:
Программа выведет:
class B
{
public:
int var_b;
};
class C
{
public:
int var_c;
};
class D: public B, public C
{
public:
int var_d;
};
using namespace std;
int main()
{
D* d = new D;
B* b = d;
C* c = d;
void* v_d = d;
void* v_b = b;
void* v_c = c;
cout << (b == d) << endl;
cout << (c == d) << endl;
cout << endl;
cout << (v_b == v_d) << endl;
cout << (v_c == v_d) << endl;
return 0;
}
1Внимание вопрос: почему в последнем случае получается ноль (false)? :)
1
1
0
Ну так почему?)
ОтветитьУдалитьмузика слова пісень философы все о философии страны европа songs lyrics lyrics все о delphi delphi музыка лирика рефераты по геологии электронные схемы радиолюбителям рефераты по геодезии лирика слова песни seropol5
ОтветитьУдалитьКак давно это было C++, классы, наследование....
ОтветитьУдалить