Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
906
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
В C++, что такое виртуальный базовый класс?
Я хочу знать, что такое "виртуальный базовый класс" и что он означает.
Позвольте мне привести пример:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Виртуальные базовые классы, используемые в виртуальном наследовании, являются способом предотвращения появления нескольких "instances" данного класса в иерархии наследования при использовании множественного наследования.
Рассмотрим следующий сценарий:
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};
Приведенная выше иерархия классов приводит к "dreaded diamond", который выглядит следующим образом:
A
/ \
B C
\ /
D
Экземпляр D будет состоять из B, который включает A, и C, который также включает A. Таким образом, у вас есть два "instances" (за неимением лучшего выражения) из A.
Когда у вас есть этот сценарий, у вас есть возможность двусмысленности. Что происходит, когда вы делаете это:
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
Виртуальное наследование существует, чтобы решить эту проблему. Когда вы указываете virtual при наследовании ваших классов, вы говорите компилятору, что вам нужен только один экземпляр.
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
Это означает, что существует только один "instance" из A, включенных в иерархию. Следовательно
D d;
d.Foo(); // no longer ambiguous
Надеюсь, что это поможет в виде мини-резюме. Для получения дополнительной информации ознакомьтесь с этим и этим . Хороший пример также доступен здесь .
О расположении памяти
Кроме того, проблема с ужасным бриллиантом заключается в том, что базовый класс присутствует несколько раз. Таким образом, при обычном наследовании вы полагаете, что у вас есть:
A
/ \
B C
\ /
D
Но в компоновке памяти у вас есть:
A A
| |
B C
\ /
D
Это объясняет, почему при вызове D::foo() у вас возникает проблема неоднозначности. Но настоящая проблема возникает, когда вы хотите использовать переменную-член A . Например, предположим, что у нас есть:
class A
{
public :
foo() ;
int m_iValue ;
} ;
Когда вы попытаетесь получить доступ к m_iValue из D , компилятор будет протестовать, потому что в иерархии он увидит два m_iValue, а не один. И если вы измените один, скажем, B::m_iValue (то есть A::m_iValue родитель B), C::m_iValue не будет изменен (то есть A::m_iValue родитель C ).
Именно здесь пригодится виртуальное наследование, так как с его помощью вы вернетесь к истинному алмазному макету, используя не только один метод foo() , но и один и только один m_iValue .
Что могло пойти не так?
Воображать:
Aимеет некоторые основные функции.Bдобавляет к нему какой-то классный массив данных (например)Cдобавляет к нему некоторые интересные функции, такие как шаблон наблюдателя (например, наm_iValue).Dнаследует отBиC, а следовательно, и отA.
При нормальном наследовании изменение m_iValue из D неоднозначно, и это необходимо решить. Даже если это так , внутри D есть два m_iValues , поэтому вам лучше помнить об этом и обновлять их одновременно.
С виртуальным наследованием изменение m_iValue из D -это нормально... Но... Допустим, у вас есть D . Через его интерфейс C вы подключили наблюдателя. И через его интерфейс B вы обновляете классный массив, который имеет побочный эффект прямого изменения m_iValue ...
Поскольку изменение m_iValue выполняется непосредственно (без использования метода виртуального доступа), наблюдатель "listening" через C вызываться не будет , поскольку код, реализующий прослушивание, находится в C, а B об этом не знает...
Вывод
Если у вас есть алмаз в вашей иерархии, это означает, что вы 95% сделали что-то не так с указанной иерархией.
Объяснение множественного наследования с помощью виртуальных баз требует знания объектной модели C++. А четкое объяснение темы лучше всего делать в статье, а не в поле для комментариев.
Самым лучшим, читаемым объяснением, которое я нашел и которое разрешило все мои сомнения на этот счет, была эта статья: http://www.phpcompiler.org/articles/virtualinheritance.html
Вам действительно не нужно будет читать что-либо еще по этой теме (если только вы не являетесь автором компилятора) после прочтения этого...
Виртуальный базовый класс - это класс, который
невозможно создать экземпляр : вы не можете
создайте из него прямой объект.
Виртуальный базовый класс - это класс, который невозможно создать экземпляр : вы не можете создайте из него прямой объект.
Я думаю, что вы путаете две очень разные вещи. Виртуальное наследование-это не то же самое, что абстрактный класс. Виртуальное наследование изменяет поведение вызовов функций; иногда оно разрешает вызовы функций, которые в противном случае были бы неоднозначными, иногда оно переносит обработку вызовов функций в класс, отличный от того, что можно было бы ожидать при невиртуальном наследовании.
Я хотел бы добавить к любезным разъяснениям OJ.
Виртуальное наследование не бывает бесценным. Как и со всеми виртуальными вещами, вы получаете хит производительности. Есть способ обойти этот хит производительности, который, возможно, менее элегантен.
Вместо того чтобы разбивать Алмаз, извлекая его виртуально, вы можете добавить к нему еще один слой, чтобы получить нечто подобное:
B
/ \
D11 D12
| |
D21 D22
\ /
DD
Ни один из классов не наследует виртуально, все наследуют публично. Классы D21 и D22 затем скроют виртуальную функцию f(), которая неоднозначна для DD, возможно, объявив функцию частной. Каждый из них определял бы функцию-оболочку, f1() и f2() соответственно, каждый вызывал бы класс-локальный (частный) f(), таким образом разрешая конфликты. Класс DD вызывает f1(), если он хочет D11::f () и f2(), если он хочет D12::f (). Если вы определяете обертки inline, вы, вероятно, получите около нуля накладных расходов.
Конечно, если вы можете изменить D11 и D12, то вы можете сделать тот же трюк внутри этих классов, но часто это не так.
В дополнение к тому, что уже было сказано о множественном и виртуальном наследовании, есть очень интересная статья в журнале доктора Добба: множественное наследование считается полезным
Это означает, что вызов виртуальной функции будет перенаправлен в класс "right".
C++ FAQ Lite FTW.
Короче говоря, он часто используется в сценариях множественного наследования, где формируется иерархия "diamond". Виртуальное наследование затем разрушит неоднозначность, созданную в нижнем классе, когда вы вызываете функцию в этом классе, и функция должна быть разрешена в класс D1 или D2 выше этого нижнего класса. Смотрите элемент FAQ для получения диаграммы и подробных сведений.
Он также используется в сестринском делегировании, мощная функция (хотя и не для слабонервных). Смотрите это FAQ.
Также смотрите пункт 40 в эффективном C++ 3-м издании (43 во 2-м издании).
Ты немного сбиваешь меня с толку. Я не знаю, не путаете ли вы некоторые понятия.
У вас нет виртуального базового класса в вашем OP. У вас просто есть базовый класс.
Вы сделали виртуальное наследование. Это обычно используется при множественном наследовании, так что несколько производных классов используют члены базового класса, не воспроизводя их.
Базовый класс с чисто виртуальной функцией не будет создан. для этого требуется синтаксис, который получает Павел. Обычно он используется для того, чтобы производные классы определяли эти функции.
Я не хочу больше ничего объяснять, потому что не совсем понимаю, о чем вы просите.
Пример использования алмазного наследования с возможностью запуска
В этом примере показано, как использовать виртуальный базовый класс в типичном сценарии: для решения задачи алмазного наследования.
#include <cassert>
class A {
public:
A(){}
A(int i) : i(i) {}
int i;
virtual int f() = 0;
virtual int g() = 0;
virtual int h() = 0;
};
class B : public virtual A {
public:
B(int j) : j(j) {}
int j;
virtual int f() { return this->i + this->j; }
};
class C : public virtual A {
public:
C(int k) : k(k) {}
int k;
virtual int g() { return this->i + this->k; }
};
class D : public B, public C {
public:
D(int i, int j, int k) : A(i), B(j), C(k) {}
virtual int h() { return this->i + this->j + this->k; }
};
int main() {
D d = D(1, 2, 4);
assert(d.f() == 3);
assert(d.g() == 5);
assert(d.h() == 7);
}
Виртуальные классы-это не то же самое, что виртуальное наследование. Виртуальные классы вы не можете создавать экземпляры, виртуальное наследование - это нечто совершенно другое.
Википедия описывает это лучше, чем я могу. http://en.wikipedia.org/wiki/Virtual_inheritance