Сценарий алмазного наследования прекрасно компилируется в G++, но выдает предупреждения/ошибки в VC++ / Eclipse


У меня есть базовый класс 'Base', который является чистым виртуальным классом:

class Base {

public:
    virtual void A() = 0;
    virtual void B() = 0;

    virtual ~Base() { } // Eclipse complains that a class with virtual members must have virtual destructor
};

У меня также есть 2 других класса, один из которых реализует A() , а другой - B ():

class DerivedA : public virtual Base
{
public:
    virtual void A() {
        printf("Hello from A");
    }
};

class DerivedB : public virtual Base
{
public:
    virtual void B() {
        printf("Hello from B");
    }
};
Виртуальное ключевое слово в объявлении должно решить проблему алмазов.

Теперь я хотел бы объединить эти два класса в другой класс, чтобы оба A () и B () были реализованы следующим образом:

class DerivedC: public DerivedA, public DerivedB {
     // Now DerivedA and DerivedB are combined
};

// Somewhere else in the code
DerivedC c;
c.A();
c.B();

Проблема: Несмотря на то, что G++ компилирует код просто отлично, Eclipse дает Ошибка: The type 'DerivedC' must implement the inherited pure virtual method 'Base::B'. При компиляции с visual studio я получаю 2 предупреждения:

warning C4250: 'DerivedC' : inherits 'DerivedB::DerivedB::B' via dominance
warning C4250: 'DerivedC' : inherits 'DerivedA::DerivedA::A' via dominance

Итак, возникает вопрос: Как правильно это делать? Производит ли приведенный выше код неопределенное поведение?

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

5 14

5 ответов:

Как правильно это делать? Производит ли приведенный выше код неопределенное поведение?

Код абсолютно корректен. Здесь нет никакого неопределенного поведения.
Безусловный вызов A() через объект класса DerivedC всегда вызовет DerivedA::A(), в то время как безусловный вызов B() через объект класса DerivedC всегда вызовет экземпляр DerivedB::B().

Visual C++ выдает предупреждение, поскольку в коде используется менее известная функция virtual Наследование, которое может быть неочевидным для большинства обычных пользователей и может их удивить. В этом случае предупреждение следует воспринимать как информативную придирку, а не как предупреждение.

Обратите внимание, что стандарт C++ не запрещает компиляторам выдавать информативные предупреждения для абсолютно корректного кода. В документации к warning C4250 приведен пример, который объясняет, почему visual C++ решил выдать это предупреждение.

Вы можете попробовать следующее :

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};

Я сам не могу тестировать с Eclipse или VC++...

Я не знаю, почему компилятор будет жаловаться на все это; это просто стандартная техника миксина. Классы Base, DerivedA и еще DerivedB абстрактны и не могут быть созданы, но обычно это так. в случае миксины. Весь смысл миксина в том, что он этого не делает. реализуйте весь интерфейс. И DerivedC реализует оба A() и B() через его наследственные члены.

Если компилятор отказывается компилировать этот код, он разбивается.

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

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

  • Предупреждения от Visual Studio являются "информативными", я думаю, но это это способ, которым Язык предназначен для работы. И это, конечно, не так. чего-то надо избегать. Если уж на то пошло, я так не думаю. господство на самом деле здесь играет роль, так как функции в Base являются чистыми виртуальный. То, что Visual Studios, кажется, пытается сказать, что в DerivedC, фактическая перегрузка A() равна DerivedA::A(), а не Base::A(). Что, по-видимому, является тем, что интуитивно ожидалось бы от меня; правила, касающиеся доминирования, на самом деле являются лишь формальным утверждением то, что можно было бы интуитивно ожидать.

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

Известно, что Visual Studio имеет ошибку компилятора , выдающую предупреждение C4250 в ситуациях, когда доминирующая функция является чисто виртуальной. Ошибка была закрыта как "не исправится"; принятое решение-подавить предупреждение, используя:

#pragma warning( disable: 4250 ) /* 'class1' : inherits 'class2::member' via dominance */

См. также обсуждение в http://msdn.microsoft.com/en-us/library/6b3sy7ae (VS.80).aspx#CommunityContentHeader .

Ваш базовый класс абстрактен : он не может быть создан. Классы B и A также абстрактны, поскольку реализуют только один метод.

Два решения находятся в файле DerivedC.cpp

void DerivedC::A(){ 
   DerivedA::A();
}
void DerivedC::B(){
   Derived:B();
}

Или вы можете использовать ключевое слово using в вашем заголовочном файле:

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};