Есть замечательная статья про виртуальное наследование. Но к сожалению, почему-то в этой статье не описывается, что происходит с виртуальными функциями при виртуальном наследовании.
Допустим есть такая иерархия классов:
class A
{
int a;
public:
virtual void foo(){ std::cout << "A";};
};
class B1 : virtual public A
{
int b1;
public:
virtual void foo(){ std::cout << "B1";};
};
class B2 : virtual public A
{
int b2;
public:
virtual void foo(){ std::cout << "B2";};
};
class C : public B1, public B2
{
int c;
};
Допустим где-то в коде встречается такое место:
A* a = new C;
a->foo();
Какая функция будет вызвана в этом случае?
Почему будет вызвана именно она? Можно ли это объяснить в терминах указателей на на таблицы виртуальных функций, как в приведенной статье?
Здравствуйте, Андрей Е, Вы писали: АЕ>Какая функция будет вызвана в этом случае? АЕ>Почему будет вызвана именно она? Можно ли это объяснить в терминах указателей на на таблицы виртуальных функций, как в приведенной статье?
Здравствуйте, panter_dsd, Вы писали:
_>Здравствуйте, Андрей Е, Вы писали: АЕ>>Какая функция будет вызвана в этом случае? АЕ>>Почему будет вызвана именно она? Можно ли это объяснить в терминах указателей на на таблицы виртуальных функций, как в приведенной статье?
_>По идее, A::foo. _>Почитать можно тут
Там ссылка на Подбельского. Что заставляет весьма сильно сомневаться в стандартности данного подхода. Его многократно переизданная книжка, упоминаемая в ссылке, полностью ориентирована на Borland.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Виртуальные функции при виртуальном наследовании
Здравствуйте, Андрей Е, Вы писали:
АЕ>Есть замечательная статья про виртуальное наследование. Но к сожалению, почему-то в этой статье не описывается, что происходит с виртуальными функциями при виртуальном наследовании.
Этот вопрос никак с виртуальным наследованием не связан. Даже при невиртуальном ромбовидном наследовании будут все те же проблемы.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Виртуальные функции при виртуальном наследовании
Здравствуйте, Erop, Вы писали:
E>Этот вопрос никак с виртуальным наследованием не связан. Даже при невиртуальном ромбовидном наследовании будут все те же проблемы.
1. Невиртуальное наследование не может быть ромбовидным. При обычном наследованиее в классе C будут содержаться два экземпляра класса A.
2. При невиртуальном наследовании на строчке:
A* a = new C;
компилятор выдаст ошибку, так как непонятно к какому из двух A приводить указатель на C.
3. Что происходит с виртуальными функциями при обычном множественном наследовании хорошо описано в другой статье того же автора: Виртуальные функции – низкоуровневый взгляд. Так что вариант с обычным наследованием понятен.
Re: Виртуальные функции при виртуальном наследовании
Никакая,
этот код компилироваться не будет, т.к. неоднозначность.
Здравствуйте, Андрей Е, Вы писали:
АЕ>Есть замечательная статья про виртуальное наследование. Но к сожалению, почему-то в этой статье не описывается, что происходит с виртуальными функциями при виртуальном наследовании.
АЕ>Допустим есть такая иерархия классов: АЕ>
АЕ>class A
АЕ>{
АЕ> int a;
АЕ>public:
АЕ> virtual void foo(){ std::cout << "A";};
АЕ>};
АЕ>class B1 : virtual public A
АЕ>{
АЕ> int b1;
АЕ>public:
АЕ> virtual void foo(){ std::cout << "B1";};
АЕ>};
АЕ>class B2 : virtual public A
АЕ>{
АЕ> int b2;
АЕ>public:
АЕ> virtual void foo(){ std::cout << "B2";};
АЕ>};
АЕ>class C : public B1, public B2
АЕ>{
АЕ> int c;
АЕ>};
АЕ>
АЕ>Допустим где-то в коде встречается такое место: АЕ>
АЕ>A* a = new C;
a->>foo();
АЕ>
АЕ>Какая функция будет вызвана в этом случае? АЕ>Почему будет вызвана именно она? Можно ли это объяснить в терминах указателей на на таблицы виртуальных функций, как в приведенной статье?
Re[2]: Виртуальные функции при виртуальном наследовании
Здравствуйте, roman313, Вы писали:
R>Никакая, R>этот код компилироваться не будет, т.к. неоднозначность.
Да, действительно не компилируется. Я немножко ошибся. Исправленная версия(точно компилируется и работает):
class A
{
int a;
public:
virtual void foo(){ std::cout << "A";}
};
class B1 : virtual public A
{
int b1;
public:
virtual void foo(){ std::cout << "B1";}
};
class B2 : virtual public A
{
int b2;
public:
};
class C : public B1, public B2
{
int c;
};
int main()
{
B2* b2 = new C;
b2->foo();
return 0;
}
Какая функция будет вызвана в строчке b2->foo()? Почему именно она?
Re[3]: Виртуальные функции при виртуальном наследовании
Error Message
'class1' : inherits 'class2::member' via dominance
Two or more members have the same name. The one in class2 is inherited because it is a base class for the other classes that contained this member.
To suppress C4250, use the warning pragma.
Because a virtual base class is shared among multiple derived classes, a name in a derived class dominates a name in a base class. For example, given the following class hierarchy, there are two definitions of func inherited within diamond: the vbc::func() instance through the weak class, and the dominant::func() through the dominant class. An unqualified call of func() through a diamond class object, always calls the dominate::func() instance. If the weak class were to introduce an instance of func(), neither definition would dominate, and the call would be flagged as ambiguous.
Copy Code
// C4250.cpp
// compile with: /c /W2#include <stdio.h>
struct vbc {
virtual void func() { printf("vbc::func\n"); }
};
struct weak : public virtual vbc {};
struct dominant : public virtual vbc {
void func() { printf("dominant::func\n"); }
};
struct diamond : public weak, public dominant {};
int main() {
diamond d;
d.func(); // C4250
}
Example
The following sample generates C4250.
Copy Code
// C4250_b.cpp
// compile with: /W2 /EHsc#include <iostream>
using namespace std;
class A {
public:
virtual operator int () {
return 2;
}
};
class B : virtual public A {
public:
virtual operator int () {
return 3;
}
};
class C : virtual public A {};
class E : public B, public C {}; // C4250int main() {
E eObject;
cout << eObject.operator int() << endl;
}
This sample shows a more complex situation. The following sample generates C4250.
Copy Code
// C4250_c.cpp
// compile with: /W2 /EHsc#include <iostream>
using namespace std;
class V {
public:
virtual int f() {
return 1024;
}
};
class B : virtual public V {
public:
int b() {
return f(); // B::b() calls V::f()
}
};
class M : virtual public V {
public:
int f() {
return 7;
}
};
// because of dominance, f() is M::f() inside D,
// changing the meaning of B::b's f() call inside a Dclass D : public B, public M {}; // C4250int main() {
D d;
cout << "value is: " << d.b(); // invokes M::f()
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, Андрей Е, Вы писали:
PD>Error Message PD>'class1' : inherits 'class2::member' via dominance
Вообще, это вроде warning, а не error.
Я читал эту статью в MSDN.
Вопрос такой: как можно объяснить такое поведение в терминах таблиц виртуальных функций?
Сколько указателей на таблицы виртуальных функций содержится в классе C? Куда они указывают? На какие функции ссылаются эти таблицы? В какой момент, как и кем инициализируются?
Re[5]: Виртуальные функции при виртуальном наследовании
Здравствуйте, Андрей Е, Вы писали:
АЕ>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Здравствуйте, Андрей Е, Вы писали:
PD>>Error Message PD>>'class1' : inherits 'class2::member' via dominance
АЕ>Вообще, это вроде warning, а не error.
Да.
АЕ>Я читал эту статью в MSDN. АЕ>Вопрос такой: как можно объяснить такое поведение в терминах таблиц виртуальных функций? АЕ>Сколько указателей на таблицы виртуальных функций содержится в классе C? Куда они указывают? На какие функции ссылаются эти таблицы? В какой момент, как и кем инициализируются?
Из твоего примера. Поставлен брекпойнт после
B2* b2 = new C;
и Quick Watch
— b2 0x00985720 {b2=-842150451 } B2 *
— A {a=-842150451 } A
— __vfptr 0x01197804 const C::`vftable' *
[0] 0x011910c8 [thunk]:B1::foo`adjustor{12}' (void) *
a -842150451 int
b2 -842150451 int
Как видишь, vtable тут одна и в ней один указатель. Впрочем, это следует из текста warning
'class1' : inherits 'class2::member' via dominance
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Я ни слова не сказал своего, только привел кусок из MSDN.
а следовало что-то написать свое, потому что цель вашего поста неясна
хотя бы выделили бы жирным то, что считаете в этой портянке важным
первое, что кидается в глаза, это "Error message", хотя в коде ТС ошибок нет
по теме на пальцах: в виртуальной таблице базового класса A должна быть ссылка на виртуальный метод foo()
если наследник один и он перекрыл метод foo(), то указатель на этот метод прописывается в таблицу
если же наследников два и имеем виртуально наследование, то
1) если оба класса переопределили foo(), то непонятно какой указатель в виртуальную таблицу прописывать. появилась неоднозначность -> код не скомпилируется
2) лишь один класс переопределил метод foo(), вот указатель на этот метод и прописывается в таблицу и код считается корректным
не забываем также, что у объекта несколько виртуальных таблиц: одна на каждый подобъект. сколько классов в иерархии — столько и таблиц имеем
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Если не секрет — за что минус ? Я ни слова не сказал своего, только привел кусок из MSDN.
Возможно, что колелга не согласен с тем, что ты привёл ответ на вопрос ТС.
То, что ты привёл -- это всего лишь иллюстрация того принципа, что определение виртуального метода берётся из того класса, который является самым дальним наследником базового. В приведённых тобой примерах всегда в одной из ветвей наследования определение было в более дальнем наследнике, чем в альтернативных. А у ТС симметричная ситаация.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Если не секрет — за что минус ? Я ни слова не сказал своего, только привел кусок из MSDN.
E>Возможно, что колелга не согласен с тем, что ты привёл ответ на вопрос ТС. E>То, что ты привёл -- это всего лишь иллюстрация того принципа, что определение виртуального метода берётся из того класса, который является самым дальним наследником базового. В приведённых тобой примерах всегда в одной из ветвей наследования определение было в более дальнем наследнике, чем в альтернативных. А у ТС симметричная ситаация.
Пример ТС выдает именно это предупреждение. Поэтому я и привел текст из MSDN с его объяснением.
Можешь проверить сам. VS 2008.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Пример ТС выдает именно это предупреждение. Поэтому я и привел текст из MSDN с его объяснением. PD>Можешь проверить сам. VS 2008.
Ну, то есть, ты не разобрался в проблеме, а просто погуглил ворнинг в MSDN'е?
Кстати, ТС вроде как сказал, что читал эту статью, а автор минуса вроде написал тебе с чем он не согласен. Можешь проверить угадал ли я
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Виртуальные функции при виртуальном наследовании
Программисты нынче делятся на четыре категории: одни чего-то там по старинке программируют, другие на форумах гневно обличают школоту не знающую, что Дональд Эрвин Кнут — не муж и жена, а три совершенно разных человека, третьи пишут книжки, четвертые их читают. Ладно, это была преамбула, надеюсь, никто не оскорбился. Так вот, о книжках, разрешите кинуть свои 5 копеек: товарищ по фамилии Дьюхерст в своей книжице "Скользкие места C++" посвятил рассматриваемому вопросу целую главу под названием "Вопросы доминирования". Все описано доступно, рекомендую. Как однажды выразился один из собратьев по RSDN, "Как хорошо уметь читать!"
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: Виртуальные функции при виртуальном наследовании
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, Андрей Е, Вы писали:
_>[skip]
_>Программисты нынче делятся на четыре категории: одни чего-то там по старинке программируют, другие на форумах гневно обличают школоту не знающую, что Дональд Эрвин Кнут — не муж и жена, а три совершенно разных человека, третьи пишут книжки, четвертые их читают. Ладно, это была преамбула, надеюсь, никто не оскорбился. Так вот, о книжках, разрешите кинуть свои 5 копеек: товарищ по фамилии Дьюхерст в своей книжице "Скользкие места C++" посвятил рассматриваемому вопросу целую главу под названием "Вопросы доминирования". Все описано доступно, рекомендую. Как однажды выразился один из собратьев по RSDN, "Как хорошо уметь читать!"