Виртуальные функции при виртуальном наследовании
От: Андрей Е  
Дата: 18.08.11 07:29
Оценка:
Есть замечательная статья про виртуальное наследование. Но к сожалению, почему-то в этой статье не описывается, что происходит с виртуальными функциями при виртуальном наследовании.

Допустим есть такая иерархия классов:
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: Виртуальные функции при виртуальном наследовании
От: Sni4ok  
Дата: 18.08.11 07:42
Оценка: +1
Здравствуйте, Андрей Е, Вы писали:

АЕ>Какая функция будет вызвана в этом случае?

UB насколько я понимаю.
Re: Виртуальные функции при виртуальном наследовании
От: panter_dsd Россия panter-dsd.narod.ru
Дата: 18.08.11 07:48
Оценка: -1
Здравствуйте, Андрей Е, Вы писали:
АЕ>Какая функция будет вызвана в этом случае?
АЕ>Почему будет вызвана именно она? Можно ли это объяснить в терминах указателей на на таблицы виртуальных функций, как в приведенной статье?

По идее, A::foo.
Почитать можно тут
С уважением.
Пантер.
Re[2]: Виртуальные функции при виртуальном наследовании
От: LaptevVV Россия  
Дата: 18.08.11 08:07
Оценка:
Здравствуйте, panter_dsd, Вы писали:

_>Здравствуйте, Андрей Е, Вы писали:

АЕ>>Какая функция будет вызвана в этом случае?
АЕ>>Почему будет вызвана именно она? Можно ли это объяснить в терминах указателей на на таблицы виртуальных функций, как в приведенной статье?

_>По идее, A::foo.

_>Почитать можно тут
Там ссылка на Подбельского. Что заставляет весьма сильно сомневаться в стандартности данного подхода. Его многократно переизданная книжка, упоминаемая в ссылке, полностью ориентирована на Borland.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: Виртуальные функции при виртуальном наследовании
От: Erop Россия  
Дата: 18.08.11 08:16
Оценка: :)
Здравствуйте, Андрей Е, Вы писали:

АЕ>Есть замечательная статья про виртуальное наследование. Но к сожалению, почему-то в этой статье не описывается, что происходит с виртуальными функциями при виртуальном наследовании.


Этот вопрос никак с виртуальным наследованием не связан. Даже при невиртуальном ромбовидном наследовании будут все те же проблемы.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Виртуальные функции при виртуальном наследовании
От: Андрей Е  
Дата: 18.08.11 09:12
Оценка:
Здравствуйте, Erop, Вы писали:

E>Этот вопрос никак с виртуальным наследованием не связан. Даже при невиртуальном ромбовидном наследовании будут все те же проблемы.


1. Невиртуальное наследование не может быть ромбовидным. При обычном наследованиее в классе C будут содержаться два экземпляра класса A.
2. При невиртуальном наследовании на строчке:
A* a = new C;

компилятор выдаст ошибку, так как непонятно к какому из двух A приводить указатель на C.
3. Что происходит с виртуальными функциями при обычном множественном наследовании хорошо описано в другой статье того же автора:
Виртуальные функции – низкоуровневый взгляд. Так что вариант с обычным наследованием понятен.
Re: Виртуальные функции при виртуальном наследовании
От: roman313  
Дата: 18.08.11 09:37
Оценка: +2
Никакая,
этот код компилироваться не будет, т.к. неоднозначность.








Здравствуйте, Андрей Е, Вы писали:

АЕ>Есть замечательная статья про виртуальное наследование. Но к сожалению, почему-то в этой статье не описывается, что происходит с виртуальными функциями при виртуальном наследовании.


АЕ>Допустим есть такая иерархия классов:

АЕ>
АЕ>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]: Виртуальные функции при виртуальном наследовании
От: Андрей Е  
Дата: 18.08.11 10:48
Оценка:
Здравствуйте, 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]: Виртуальные функции при виртуальном наследовании
От: Pavel Dvorkin Россия  
Дата: 18.08.11 11:10
Оценка: -1
Здравствуйте, Андрей Е, Вы писали:

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 {};   // C4250

int 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 D
class D : public B, public M {};   // C4250

int main() {
   D d;
   cout << "value is: " << d.b();   // invokes M::f()
With best regards
Pavel Dvorkin
Re[4]: 2 uzhas
От: Pavel Dvorkin Россия  
Дата: 18.08.11 14:36
Оценка:
Если не секрет — за что минус ? Я ни слова не сказал своего, только привел кусок из MSDN.
With best regards
Pavel Dvorkin
Re[4]: Виртуальные функции при виртуальном наследовании
От: Андрей Е  
Дата: 19.08.11 04:35
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Андрей Е, Вы писали:


PD>Error Message

PD>'class1' : inherits 'class2::member' via dominance

Вообще, это вроде warning, а не error.
Я читал эту статью в MSDN.
Вопрос такой: как можно объяснить такое поведение в терминах таблиц виртуальных функций?
Сколько указателей на таблицы виртуальных функций содержится в классе C? Куда они указывают? На какие функции ссылаются эти таблицы? В какой момент, как и кем инициализируются?
Re[5]: Виртуальные функции при виртуальном наследовании
От: Pavel Dvorkin Россия  
Дата: 19.08.11 05:07
Оценка:
Здравствуйте, Андрей Е, Вы писали:

АЕ>Здравствуйте, 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

а другую не inherits
With best regards
Pavel Dvorkin
Re[5]: 2 uzhas
От: uzhas Ниоткуда  
Дата: 19.08.11 07:55
Оценка: -1
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Я ни слова не сказал своего, только привел кусок из MSDN.

а следовало что-то написать свое, потому что цель вашего поста неясна
хотя бы выделили бы жирным то, что считаете в этой портянке важным
первое, что кидается в глаза, это "Error message", хотя в коде ТС ошибок нет

по теме на пальцах: в виртуальной таблице базового класса A должна быть ссылка на виртуальный метод foo()
если наследник один и он перекрыл метод foo(), то указатель на этот метод прописывается в таблицу
если же наследников два и имеем виртуально наследование, то
1) если оба класса переопределили foo(), то непонятно какой указатель в виртуальную таблицу прописывать. появилась неоднозначность -> код не скомпилируется
2) лишь один класс переопределил метод foo(), вот указатель на этот метод и прописывается в таблицу и код считается корректным

не забываем также, что у объекта несколько виртуальных таблиц: одна на каждый подобъект. сколько классов в иерархии — столько и таблиц имеем
Re[5]: 2 uzhas
От: Erop Россия  
Дата: 19.08.11 10:23
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Если не секрет — за что минус ? Я ни слова не сказал своего, только привел кусок из MSDN.


Возможно, что колелга не согласен с тем, что ты привёл ответ на вопрос ТС.
То, что ты привёл -- это всего лишь иллюстрация того принципа, что определение виртуального метода берётся из того класса, который является самым дальним наследником базового. В приведённых тобой примерах всегда в одной из ветвей наследования определение было в более дальнем наследнике, чем в альтернативных. А у ТС симметричная ситаация.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: 2 uzhas
От: Pavel Dvorkin Россия  
Дата: 19.08.11 11:01
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Если не секрет — за что минус ? Я ни слова не сказал своего, только привел кусок из MSDN.


E>Возможно, что колелга не согласен с тем, что ты привёл ответ на вопрос ТС.

E>То, что ты привёл -- это всего лишь иллюстрация того принципа, что определение виртуального метода берётся из того класса, который является самым дальним наследником базового. В приведённых тобой примерах всегда в одной из ветвей наследования определение было в более дальнем наследнике, чем в альтернативных. А у ТС симметричная ситаация.

Пример ТС выдает именно это предупреждение. Поэтому я и привел текст из MSDN с его объяснением.
Можешь проверить сам. VS 2008.
With best regards
Pavel Dvorkin
Re[7]: 2 uzhas
От: Erop Россия  
Дата: 19.08.11 11:49
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Пример ТС выдает именно это предупреждение. Поэтому я и привел текст из MSDN с его объяснением.

PD>Можешь проверить сам. VS 2008.

Ну, то есть, ты не разобрался в проблеме, а просто погуглил ворнинг в MSDN'е?
Кстати, ТС вроде как сказал, что читал эту статью, а автор минуса вроде написал тебе с чем он не согласен. Можешь проверить угадал ли я
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Виртуальные функции при виртуальном наследовании
От: slava_phirsov Россия  
Дата: 25.08.11 15:56
Оценка:
Здравствуйте, Андрей Е, Вы писали:

[skip]

Программисты нынче делятся на четыре категории: одни чего-то там по старинке программируют, другие на форумах гневно обличают школоту не знающую, что Дональд Эрвин Кнут — не муж и жена, а три совершенно разных человека, третьи пишут книжки, четвертые их читают. Ладно, это была преамбула, надеюсь, никто не оскорбился. Так вот, о книжках, разрешите кинуть свои 5 копеек: товарищ по фамилии Дьюхерст в своей книжице "Скользкие места C++" посвятил рассматриваемому вопросу целую главу под названием "Вопросы доминирования". Все описано доступно, рекомендую. Как однажды выразился один из собратьев по RSDN, "Как хорошо уметь читать!"
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: Виртуальные функции при виртуальном наследовании
От: Доктор ТуамОсес Гондурас Мой новый проект "ВЕПРЬ-1"
Дата: 25.08.11 18:05
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>Здравствуйте, Андрей Е, Вы писали:


_>[skip]


_>Программисты нынче делятся на четыре категории: одни чего-то там по старинке программируют, другие на форумах гневно обличают школоту не знающую, что Дональд Эрвин Кнут — не муж и жена, а три совершенно разных человека, третьи пишут книжки, четвертые их читают. Ладно, это была преамбула, надеюсь, никто не оскорбился. Так вот, о книжках, разрешите кинуть свои 5 копеек: товарищ по фамилии Дьюхерст в своей книжице "Скользкие места C++" посвятил рассматриваемому вопросу целую главу под названием "Вопросы доминирования". Все описано доступно, рекомендую. Как однажды выразился один из собратьев по RSDN, "Как хорошо уметь читать!"


А мне больше ндравитцо это: "Перед каждым образованным человеком
(т.е. умеющим читать и писать) рано или поздно встаёт вопрос:
да чего же всё же делать? Читать? Или писать?
Мой новый проект "ВЕПРЬ-1"
Re[4]: Виртуальные функции при виртуальном наследовании
От: slava_phirsov Россия  
Дата: 26.08.11 11:24
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

Ну вот, один гражданин уже принял на свой счет и, кажется, оскорбился. А может, и не один. Прошу извинить, если что
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.