Что такое виртуальный базовый класс в C ++?

Проблема заключается в том, что прокси-серверы AOP Spring не распространяются, а скорее завершают ваш экземпляр службы, чтобы перехватывать вызовы. Это приводит к тому, что любой вызов «этого» из вашего экземпляра службы непосредственно вызывается в этом экземпляре и не может быть перехвачен прокси-сервером (прокси-сервер даже не знает о таком вызове). Уже упоминалось одно решение. Еще одним из излюбленных было бы просто заставить Spring вставить экземпляр службы в саму службу и вызвать ваш метод на введенном экземпляре, который будет прокси-сервером, который обрабатывает ваши транзакции. Но имейте в виду, что это может иметь и плохие побочные эффекты, если ваш сервисный компонент не является одиночным:

<bean id="userService" class="your.package.UserService">
  <property name="self" ref="userService" />
    ...
</bean>

public class UserService {
    private UserService self;

    public void setSelf(UserService self) {
        this.self = self;
    }

    @Transactional
    public boolean addUser(String userName, String password) {
        try {
        // call DAO layer and adds to database.
        } catch (Throwable e) {
            TransactionAspectSupport.currentTransactionStatus()
                .setRollbackOnly();

        }
    }

    public boolean addUsers(List<User> users) {
        for (User user : users) {
            self.addUser(user.getUserName, user.getPassword);
        }
    } 
}
385
задан manlio 1 March 2014 в 13:37
поделиться

9 ответов

Виртуальные базовые классы, используемые в виртуальном наследовании, являются способом предотвратить несколько "экземпляров" данного класса, появляющегося в иерархии наследования при использовании множественного наследования.

Рассматривают следующий сценарий:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

вышеупомянутая иерархия классов приводит к "страшному ромбу", который похож на это:

  A
 / \
B   C
 \ /
  D

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

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

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

Виртуальное наследование там для решения этой проблемы. Когда Вы определяете виртуальный при наследовании классов, Вы говорите компилятору, что только хотите единственный экземпляр.

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

Это означает, что существует только один "экземпляр" включенного в иерархию. Следовательно

D d;
d.Foo(); // no longer ambiguous

Hope, которая помогает как мини-сводка. Для получения дополнительной информации имейте чтение это и это . Хороший пример также доступен здесь .

519
ответ дан Atul Kumar 1 March 2014 в 13:37
поделиться

Я хотел бы добавить к добрым разъяснениям OJ.

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

Вместо того, чтобы повредить ромб путем получения фактически, можно добавить другой слой к ромбу, для получения чего-то вроде этого:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

Ни один из классов не наследовался фактически, все наследовались публично. Классы D21 и D22 тогда скроют виртуальную функцию f (), который неоднозначен для DD, возможно, путем объявления частной функции. Они каждый определили бы функцию обертки, f1 () и f2 () соответственно, каждый звонящий локальный для класса (частный) f (), таким образом разрешив конфликты. DD класса называет f1 (), если это хочет D11:: f () и f2 (), если это хочет D12:: f (). При определении оберток, встроенных, Вы, вероятно, доберетесь о нуле наверху.

, Конечно, если можно изменить D11 и D12 тогда, можно сделать тот же прием в этих классах, но часто дело не в этом.

6
ответ дан Mathieu K. 1 March 2014 в 13:37
поделиться
  • 1
    Слова благодарности - поиск ответа в течение многих часов. – markp3rry 24 April 2014 в 10:25

Вы немного сбиваете с толку. Я' не знаю, перепутываете ли Вы некоторые понятия.

у Вас нет виртуального базового класса в Вашем OP. У Вас просто есть базовый класс.

Вы сделали виртуальное наследование. Это обычно используется во множественном наследовании так, чтобы несколько производных классов использовали членов базового класса, не воспроизводя их.

базовый класс А с чистой виртуальной функцией не быть инстанцированным. это требует синтаксиса, который достигает Paul. Это обычно используется так, чтобы производные классы определили те функции.

я не хочу объяснять больше об этом, потому что я не полностью получаю то, что Вы спрашиваете.

1
ответ дан Baltimark 1 March 2014 в 13:37
поделиться

А виртуальный базовый класс является классом, который нельзя инстанцировать: Вы не можете создать прямое дополнение из него.

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

9
ответ дан wilhelmtell 1 March 2014 в 13:37
поделиться

Это означает, что вызов к виртуальной функции будет переведен к "правильному" классу.

C++ FAQ, Облегченный FTW.

Короче говоря, это часто используется в сценариях множественного наследования, где "ромбовидная" иерархия формируется. Виртуальное наследование тогда повредит неоднозначность, созданную в нижнем классе, когда Вы вызовете функцию в том классе, и функция должна быть разрешена к любому классу D1 или D2 выше того нижнего класса. Посмотрите объект FAQ для схемы и деталей.

Это также используется в родственная делегация , мощная функция (хотя не для слабонервных). См. этот FAQ.

Также посмотрите Объект 40 в Эффективном C++ 3-й выпуск (43 в 2-м выпуске).

1
ответ дан Atul Kumar 1 March 2014 в 13:37
поделиться

Виртуальные классы не то же как виртуальное наследование. Виртуальные классы, которых Вы не можете инстанцировать, виртуальное наследование, являются чем-то еще полностью.

Википедия описывает его лучше, чем я могу. http://en.wikipedia.org/wiki/Virtual_inheritance

0
ответ дан bradtgmurray 1 March 2014 в 13:37
поделиться

В дополнение к тому, что было уже сказано о нескольких и виртуальном наследовании (наследованиях), существует очень интересная статья о Журнале доктора Dobb: Множественное наследование, Продуманное Полезный

4
ответ дан Luc Hermitte 1 March 2014 в 13:37
поделиться

О расположении памяти

Как примечание стороны, проблема со Страшным Ромбом состоит в том, что базовый класс присутствует многократно. Таким образом с регулярным наследованием, Вы полагаете, что имеете:

  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 родитель [1 110]), [то 1111] не будет изменен (который является A::m_iValue родитель [1 113]).

Это - то, куда виртуальное наследование происходит удобное, как с ним, Вы возвратитесь к истинному ромбовидному расположению, с не только один foo() метод только, но также и один и только один m_iValue.

, Что могло пойти не так, как надо?

Вообразите:

  • A имеет некоторую основную характеристику.
  • B добавляет к нему, некоторый прохладный массив данных (например)
  • C добавляет к нему некоторую замечательную опцию как шаблон "наблюдатель" (например, на [1 119]).
  • D наследовался от [1 121] и C, и таким образом от [1 123].

С нормальным наследованием, изменяя m_iValue от [1 125] неоднозначно, и это должно быть разрешено. Даже если это, существует два m_iValues внутреннее D, таким образом, необходимо помнить, что и обновляют два одновременно.

С виртуальным наследованием, изменяя m_iValue от [1 129] в порядке... Но... Скажем, то, что Вы имеете D. Через C интерфейс, Вы присоединили наблюдателя. И через B интерфейс, Вы обновляете прохладный массив, который имеет побочный эффект прямого изменения m_iValue...

, Поскольку изменение [1 134] сделано непосредственно (не используя виртуальный метод доступа), наблюдателя, "слушающего" до [1 135], не позвонят, потому что код, реализовывая слушание находится в [1 136], и B не знает об этом...

Заключение

, Если у Вас есть ромб в Вашей иерархии, это означает, что у Вас есть 95%, чтобы сделать что-то не так с упомянутой иерархией.

238
ответ дан Searene 1 March 2014 в 13:37
поделиться

Для объяснения множественного наследования с помощью виртуальных баз требуется знание объектной модели C ++. И ясное объяснение темы лучше всего делать в статье, а не в поле для комментариев.

Лучшее, удобочитаемое объяснение, которое я нашел, разрешившее все мои сомнения по этому поводу, - это статья: http: //www.phpcompiler .org / article / virtualinheritance.html

Вам действительно не нужно будет читать что-либо еще по этой теме (если вы не являетесь автором компилятора) после прочтения этого ...

32
ответ дан 22 November 2019 в 23:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: