2012-01-01 4 views
1

В следующем случае, virtual используется для решения проблемы алмазной иметь суб-объект класс A совместно с B и C.Значения «виртуальный» в классах

Например:

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

Будет ли этот тип наследования разрешен во время компиляции или времени выполнения? Я имею в виду, насколько отличается значение виртуального при использовании функций и классов? Существует ли концепция динамического связывания, когда виртуальный используется для наследования из базового класса?

+3

Взгляните на http://en.wikipedia.org/wiki/Virtual_inheritance. –

+0

Действительно. -1 для справочного вопроса. –

+0

@OliCharlesworth: Я уже прочитал. Это не помогает понять, как оно реализовано, и его сходство с механизмом виртуальных функций, который включает динамическое связывание. Все, что я понял, это то, что под-объект разделяется между двумя классами, но я хочу знать, по какой цене? – user1086635

ответ

1

Виртуальное Наследование разрешено во время выполнения.

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

Это описано failry хорошо на How C++ virtual inheritance is implemented in compilers?

0

Это мое понимание того, что виртуальные классы решают время компиляции неоднозначности. Например, предположим, что оба B и C имеют метод getSize.

Без виртуального вызова D.getSize пытается использовать метод getSize базового класса. Поскольку оба B и C имеют метод getSize, компилятор не может правильно разрешить двусмысленность, и код не будет компилироваться.

С виртуальным вызовом D.getSize используется децидентный метод getSize, позволяющий правильно компилировать код.

1

Наследование не обязательно должно быть «разрешено», как вы выразились, поэтому вопрос не очень ясен.

Важным отличием является обычное наследование и виртуальное наследование. Если вы унаследовали обычно, т.е. B : A, C : A, то класс D : B, Cимеет два подклассы типа A, а именно D::B::A и D::C::A. С другой стороны, если B и C наследуют фактически от A, то окончательная композиция подкласса будет отложена до тех пор, пока вы не определите конечный тип. То есть, B : virtual A и C : virtual A сами имеют виртуальный подкласс A, который станет реальным, если вы должны были создать экземпляр либо B, либо C. С другой стороны, если вы выйдете из классов дальше, то наиболее производный класс будет содержать только один подкласс типа A.

Возможно, вам понравится рассмотреть аналогию с элементом функции. Если каждый производный класс добавляет функцию-член с тем же именем, что и базовая функция, вы получаете несколько различных функций. С другой стороны, если базовая функция равна virtual, тогда у вас будет только одна функция, которая определена в самом производном классе. У вас все еще есть вид функции в каждом промежуточном классе (при условии, что функция не является чистой-виртуальной), но только последний класс определяет «активное» определение.

Виртуальное наследование влияет на конструкторы, а именно, что конструктор виртуальной базы (т.A() в данном примере), называется непосредственно наиболее производного класса, т.е. D и не от B или C. Если вы это сделаете, это потому, что только D «знает», что он содержит только один A -subclass, поэтому он непосредственно «ответственен» за него. B и C просто держите виртуальный заполнитель, который уступает место высшему, самому производному классу.

Доступ к функции-члену с помощью любого базового указателя/ссылки ведет себя так, как ожидалось, и разрешается обычным способом (т. Е. Динамически, в общем случае), но это не имеет ничего общего с виртуальным наследованием. Фактический поиск функции может быть немного более сложным, так как может потребоваться дополнительный уровень косвенности, но это ничего не меняет.

+0

Аналогия с функциями на самом деле не очень хорошая. В случае функций-членов 'virtual' означает, что вызов неквалифицированной функции будет отправлен в конечный переулок, т. Е. Определяет, какой * из функций будет вызываться, но может быть много переопределений. С другой стороны, с «виртуальным» наследованием ключевое слово означает, что база будет * уникальной *. –

+0

@ DavidRodríguez-dribeas: Справедливая точка. Хотя виртуальное наследование еще более тонкое, поскольку вы можете смешивать виртуальные базы с не виртуальными базами одного и того же типа ... в конечном счете, нет никакого способа понять все это, и никакая аналогия не может быть заменой. –

+0

@KerrekSB Невиртуальные базовые классы не подвержены возникновению виртуальных базовых классов одного и того же типа в одной и той же иерархии, так же как не виртуальные функции не подвержены возникновению виртуальных функций с тем же именем в одной иерархии. Правила не совсем одинаковы (сопоставление виртуального базового класса основано на типе (= квалифицированное имя), сопоставление виртуальных функций основано на неквалифицированном имени, функция может быть неявно виртуальной, а не базовым классом ...), но правила несколько схожи , – curiousguy

1

Будет ли этот тип наследования разрешен во время компиляции или во время выполнения?

Наследование определяет форму (объем памяти) типов, и это не будет решена всегда во время компиляции. С другой стороны, доступ к элементам базового типа будет разрешен во время выполнения, потому что промежуточные типы в иерархии не могут знать, где базовый подобъект будет уложен в память в самом производном типе.

Я имею в виду, насколько различен смысл виртуальных при использовании функций и классов?

Типы и функции различны на всех уровнях, поэтому здесь не так много сказать. Единственное, что можно сказать о том, что есть часть работы, которая не может быть полностью решена во время компиляции. В частности, сходство заключается в том, что код, который использует виртуальную функцию, зависит от vptr (указателя виртуальной таблицы), чтобы найти правильную виртуальную таблицу для виртуальной таблицы для наиболее производного типа, и аналогичным образом доступ к базовому объекту требует использование указателя, хранящегося в каждом подобъекте типа, который получается практически из базы.

Вы можете узнать больше (о конкретной реализации, все это не входит в стандарт) в Itanium C++ ABI и, в частности, в Non-POD Class Types.

В качестве краткого описания, когда тип D наследуется практически от типа B, подобъект D будет содержать скрытый указатель на подобъект B. Потребность в этом указателе возникает из-за того, что относительное положение подобъекта B по отношению к D может меняться с последующим наследованием.

+0

«Когда бы ни был тип D, наследуемый практически от типа B, D-подобъект D будет содержать скрытый указатель на B-подобъект B», то есть, у Itanium C++ ABI нет скрытого указателя! – curiousguy

Смежные вопросы