Объясните про virtual public наследование
От: sheglov.nik  
Дата: 05.04.11 16:05
Оценка:
Привет!

Имеется такое определение класса:
//Derived.h
#include "Base.h"
class Derived : virtual public Base {
//..
};


Не понимаю как понимать такое определение класса Derived? В каком случае нужно использовать public-наследование, а в каком "virtual public"-наследование?

Заранее благодарен!
Re: Объясните про virtual public наследование
От: Vamp Россия  
Дата: 05.04.11 16:35
Оценка: 5 (3)
SN>Не понимаю как понимать такое определение класса Derived? В каком случае нужно использовать public-наследование, а в каком "virtual public"-наследование?
Виртуальное наследование влияет на поведение классов при множественном наследовании.

Классический пример:


struct A {
   int i;
};

struct B: virtual A {
};

struct C: virtual A {
};


struct D: B, C {
};


int main () {
   D d;
   d.i = 10; // *
   d.B::i  = 10 // **
}


Обрати внимание, в классе D члена i нет. Более того, нет его в С и B. Он есть только в A. Если бы наследование этих классов было бы не виртуальным, то строка помеченная * не компилировалась бы — потому что комплятор не знал бы, какое i имеется в виду — то, которое попало в B из A, или то, которое попало в С из А. Иными словами, в классе D имелось бы две версии i, и надо было бы указывать, какую конкретно ты имеешь в виду — используя синтаксис из строки **.

При виртуальном наследовании гарантируется, что класс D имеет в себе только одну копию i (а точнее, базового класса A).
Да здравствует мыло душистое и веревка пушистая.
Re: Объясните про virtual public наследование
От: skeptic  
Дата: 05.04.11 16:41
Оценка: 2 (1)
Здравствуйте, sheglov.nik, Вы писали:

SN>Привет!


SN>Имеется такое определение класса:

SN>
SN>//Derived.h
SN>#include "Base.h"
SN>class Derived : virtual public Base {
SN>//..
SN>};
SN>


SN>Не понимаю как понимать такое определение класса Derived? В каком случае нужно использовать public-наследование, а в каком "virtual public"-наследование?


SN>Заранее благодарен!


Это решение проблемы т.н ромбовидного наследования.

Пример проблемного кода

//Файл Derived.hpp
#include "Base.h"

class LeftDerived : public Base
{
//...
};

class RightDerived : public Base
{
//...
};

//Файл WTFDerived.hpp
#include "Derived.hpp"

class WTFDerived : public LeftDerived, public RightDerived
{
//Подумай что у нас будет твориться тут
};


В принципе во всех вменяемых книгах по плюсам эта проблема рассматривается.
Re: Объясните про virtual public наследование
От: MasterZiv СССР  
Дата: 05.04.11 17:17
Оценка: 2 (1)
On 05.04.2011 20:05, sheglov.nik wrote:

> Не понимаю как понимать такое определение класса Derived? В каком случае нужно

> использовать public-наследование, а в каком "virtual public"-наследование?

Всегда, когда готов разделить базовый подкласс с другими классами-наследниками.
(от этого базового класса и от тебя).
На самом деле сложно объяснить. Если со множественным наследованием не имел
дела, то и не поймёшь.
Posted via RSDN NNTP Server 2.1 beta
Re: Объясните про virtual public наследование
От: MescalitoPeyot Украина  
Дата: 05.04.11 18:50
Оценка: 24 (1) :))) :))) :)))
Здравствуйте, sheglov.nik, Вы писали:

SN>Не понимаю как понимать такое определение класса Derived? В каком случае нужно использовать public-наследование, а в каком "virtual public"-наследование?


Невиртуальное:

class Конный Д'Артаньян : Д'Артаньян, Лошадь;
class Конный Атос : Атос, Лошадь;
class Конный отряд мушкетеров на работе : Конный Д'Артаньян, Конный Атос;

Виртуальное:

class Конный Д'Артаньян : Д'Артаньян, virtual Лошадь;
class Конная Баба : Баба, virtual Лошадь;
class Конный отряд мушкетеров на отдыхе : Конный Д'Артаньян, Конная Баба;
... << RSDN@Home 1.2.0 alpha 4 rev. 1138>>
Re: Объясните про virtual public наследование
От: Erop Россия  
Дата: 05.04.11 20:12
Оценка: 43 (5) -1
Здравствуйте, sheglov.nik, Вы писали:

SN>Не понимаю как понимать такое определение класса Derived? В каком случае нужно использовать public-наследование, а в каком "virtual public"-наследование?


SN>Заранее благодарен!


Во всех случаях, когда ты не уверен, зачем почему и для чего тебе надо виртуальное наследование, не надо его использовать. И если даже уверен, то всё равно не надо. Лучше юзай goto, вреда будет меньше...

это было ответом на заданный тобой вопрос, но я думаю, что ты ещё рассчитываешь и на комментарий.
Проблема с OOП-наследованием в С++ состоит в двух вещах. Во-первых, оно ведёт себя не так, как, С++ наследование. И, во вторых, в том, что в С++ наследование применяется много для чего.

Вот смотри. Рассмотрим такую иерархию. Из класса А выводятся классы В и С, а из В и С выводится класс D.
Что это значит с точки зрения ООП?
Это значит, что В -- это всегда А, и С -- это тоже всегда А, так что D, это тоже А.
Но в С++ это значит кое-что другое. Что с объектом В можно работать, как с А, потому, что где-то внутри каждого экземпляра В есть экземпляр А.
И в каждом экземпляре С есть свой экземпляр А.
Так что в D есть сразу два и из В и из С.

переведём на русский.
Пусть А = человек
      В = женщина
      С = военнослужащий
      D = женщина-военнослужащий
Так вот в реале и в ООП женщина-военнослужащий нифига не дважды человек, а вот в С++ таки дважды.
Это суть проблемы. С++ так спроектирован, что УДОБНО работать только с такими иерархиями, в которых эта вот проблема с дважды человеком просто не возникает. Обычно так их и строят. Скажем можно сделать так, что пол или род занятий чела будет выражаться полем, а не типом. И проблемы не возникнет.

И всё казалось бы уже хорошо, но тут проступает такая попа, что наследование в С++ юзают для того, чтобы выражать отношение "класс А реализует интерфейс В". А интерфейсы кто-то зачем-то придумал наследовать.
Лично я не вижу в наследовании интерфейсов никакого смысла или пользы.
Но это очень распространённая практика, к сожалению

К чему же она приводит?

Ну, положим у нас есть интерфейс I1 и из него выведен класс, который его реализует, скажем С1.
Теперь мы вывели из I1 интерфейс I2, и хотим его реализовывать в С2. Но было бы логично унаследовать в С2 реализацию I1 из С1.
То есть у нас опять появляется тот самый "дважды человек"...

В общем в какой-то момент авторы С++ сломались и придумали такой хак. Если мы скажем, что мы от человека или от I1 отнаследовались виртуально, то это будет обозначать, что внутри экземпляра класса С2 или D будет только один экземпляр виртуальной базы.

То есть женщина-военнослужащий станет единожды-человеком, если и женщина и веоннослужащий являются человеком не просто так, а виртуально
К сожалению виртуальные базы сильно противоречат модели данных С++, так что хорошо их сделать так и не получилось. С ними связано множество засад, одна из них -- это то, что любой конструктор любого наследника, как прямого, так и непрямого, должен уметь конструировать свои виртуальные базы, знать какие конструкторы и с какими параметрами звать. Вторая засада состоит в неэффективности многих неожиданных мест. Третья в том, что некоторые указатели начинают занимать по 8 или 12 байт. В общем ВСЁ РАВНО ВЫХОДИТ КРИВО.
Так что лучше виртуальными базами совсем не пользоваться...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Объясните про virtual public наследование
От: sheglov.nik  
Дата: 06.04.11 03:32
Оценка:
Здравствуйте, Vamp, Вы писали:
V>Классический пример:
V>
V>struct A {
V>   int i;
V>};
V>struct B: virtual A {
V>};
V>struct C: virtual A {
V>};
V>struct D: B, C {
V>};
V>

Спасибо, с этим примером понятно. Но в примере, который я привел, фигурирует только один наследник — Derived (я специально написал класс Derived в собственном заголовочном файле). И от этого наследника Derived никто не наследуется. Что хотел сказать программист когда выбрал public virtual наследование? Вот это все же не очень понимаю...
Re[2]: Объясните про virtual public наследование
От: sheglov.nik  
Дата: 06.04.11 03:35
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Всегда, когда готов разделить базовый подкласс с другими классами-наследниками.

MZ>(от этого базового класса и от тебя).
Вроде таких случаев большинство... но public virtual встречаю редко...
Re[3]: Объясните про virtual public наследование
От: fdn721  
Дата: 06.04.11 04:06
Оценка:
Здравствуйте, sheglov.nik, Вы писали:

SN>Здравствуйте, MasterZiv, Вы писали:


MZ>>Всегда, когда готов разделить базовый подкласс с другими классами-наследниками.

MZ>>(от этого базового класса и от тебя).
SN>Вроде таких случаев большинство... но public virtual встречаю редко...


На самом деле ромбовидное наследование это первый признак плохой архитектуры.

Мне всего один раз в жизни попадалась ситуация, когда без public virtual нельзя было обойтись.
Это была библиотека метоинформации для C++ написанная на шаблонах. И public virtual было там наименьшее из зол.
Re[3]: Объясните про virtual public наследование
От: MasterZiv СССР  
Дата: 06.04.11 05:19
Оценка:
On 06.04.2011 7:35, sheglov.nik wrote:
> MZ>Всегда, когда готов разделить базовый подкласс с другими классами-наследниками.
> MZ>(от этого базового класса и от тебя).
> Вроде таких случаев большинство... но public virtual встречаю редко...

Смотришь в корень, именно так и есть.
Но люди хотят облегчить себе жизнь при отсутствии множественного наследования.
К тому же вопрос о разделении родительского подкласса с другими подклассами
на самом деле достаточно сложный -- нужно рассматривать всё иерархию классов.
Posted via RSDN NNTP Server 2.1 beta
Re: Объясните про virtual public наследование
От: MasterZiv СССР  
Дата: 06.04.11 05:35
Оценка: :)
On 05.04.2011 20:05, sheglov.nik wrote:

Тут многие высказались о проблемах насделования в С++.
Реально проблем с наследованием в С++ только одна --
многие наследование в С++ "не догоняют". Некоторые
после первого шага делают выводы: "я это не понимаю,
значит это -- плохо. И объявляют множественное
наследование "плохой архитектурой".
Других проблем не существует.

Отдельно для Егора: Егор, ты неправ.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Объясните про virtual public наследование
От: Erop Россия  
Дата: 06.04.11 06:01
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Отдельно для Егора: Егор, ты неправ.


В чём именно? В том, что женщина, хоть и военнослужащий таки человек? Или в том, что лучше юзать goto?
Позицию по goto готов обосновать. Вред от него размазан только по одной функции... Если чё, рефакторинг проще...

Или ты про то, что виртуальными базами стоит пользоваться? Ну приводи примеры, когда это хорошо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Объясните про virtual public наследование
От: jazzer Россия Skype: enerjazzer
Дата: 06.04.11 06:05
Оценка: 2 (1)
Здравствуйте, sheglov.nik, Вы писали:

SN>Спасибо, с этим примером понятно. Но в примере, который я привел, фигурирует только один наследник — Derived (я специально написал класс Derived в собственном заголовочном файле). И от этого наследника Derived никто не наследуется. Что хотел сказать программист когда выбрал public virtual наследование? Вот это все же не очень понимаю...


Ну, может, это сейчас никто не наследуется, а когда программер этот код писал, он предполагал, что в конце концов должен возникнуть ромб
jazzer (Skype: enerjazzer) Ночная тема для RSDN
You will always get what you always got
  If you always do  what you always did
Re[3]: Объясните про virtual public наследование
От: MasterZiv СССР  
Дата: 06.04.11 07:44
Оценка: :)
On 06.04.2011 10:01, Erop wrote:

> В чём именно?


Да во всём.
А главное -- в нежелании понять, для чего и почему так сделано в С++.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Ой, Мастер! Просим! Просим!
От: Erop Россия  
Дата: 06.04.11 07:48
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Да во всём.

А ты давай конкретнее, не стесняйся... По пунктам, так сказать

MZ>А главное -- в нежелании понять, для чего и почему так сделано в С++.

Я про своё желание или понимание того, зачем и что так сделано в С++ ничего не писал. Я всего лишь объяснял коллеге что такое виртуальное наследование и когда, почему и как его лучше избегать...

Но ты, опять, же, можешь рассказать как оно правильно-то бывает. А то мы без тебя не в курсах, а я не прав и всех ввожу потому в заблуждение...

Так что, давай, жги... SUBJ.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: Кстати, про логику... ;)
От: Erop Россия  
Дата: 06.04.11 07:50
Оценка: :)
Здравствуйте, MasterZiv, Вы писали:

>> В чём именно?

MZ>Да во всём.
Кстати, я считаю, что ты довольно таки умный. Но ты утверждаешь, что я во всём не прав. Что из этого следует?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Кстати, про логику... ;)
От: rg45 СССР  
Дата: 06.04.11 08:20
Оценка: :))) :))) :))
Здравствуйте, Erop, Вы писали:

E>Кстати, я считаю, что ты довольно таки умный. Но ты утверждаешь, что я во всём не прав. Что из этого следует?


О! Классная задачка!

Дано:
  1. Егор считает, что MasterZiv умный;
  2. MasterZiv утверждает, что Егор во всем не прав.

Какие утверждения логически следуют из исходных:
  1. MasterZiv умный
  2. MasterZiv прав во всем;
  3. MasterZiv прав, но не во всем;
  4. MasterZiv во всем не прав;
  5. Егор умный;
  6. Егор прав, считая, MasterZiv-а умным;
  7. Егор прав во всем;
  8. Егор прав, но не во всем;
  9. Егор во всем не прав;
--
Справедливость выше закона. А человечность выше справедливости.
Re[3]: Объясните про virtual public наследование
От: Vamp Россия  
Дата: 06.04.11 14:42
Оценка: :)
SN>Спасибо, с этим примером понятно. Но в примере, который я привел, фигурирует только один наследник — Derived (я специально написал класс Derived в собственном заголовочном файле). И от этого наследника Derived никто не наследуется. Что хотел сказать программист когда выбрал public virtual наследование? Вот это все же не очень понимаю...
Ну, читать чужие мысли трудно. Вариантов несколько:
1. Он расчитывал на множественное наследование в будущем
2. Он не очень хорошо понимает смысл виртуального наследования (например, путает его с виртуальными функциями)
3. Он пытался умилостивить Богов С++, принося им виртуальное жертвоприношение
4. Он сделал это во сне.
5. 42
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Объясните про virtual public наследование
От: BulatZiganshin  
Дата: 06.04.11 14:48
Оценка:
Здравствуйте, MescalitoPeyot, Вы писали:

MP>class Конный Д'Артаньян : Д'Артаньян, Лошадь;

MP>class Конный Атос : Атос, Лошадь;
MP>class Конный отряд мушкетеров на работе : Конный Д'Артаньян, Конный Атос;

а если здесь сделать virtual?
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Объясните про virtual public наследование
От: Vamp Россия  
Дата: 06.04.11 14:49
Оценка: 1 (1) +1 :)
BZ>а если здесь сделать virtual?
Будет ужас-ужас-ужас!
Да здравствует мыло душистое и веревка пушистая.