Сценарий алмазного наследования прекрасно компилируется в 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 ответов:
Как правильно это делать? Производит ли приведенный выше код неопределенное поведение?
Код абсолютно корректен. Здесь нет никакого неопределенного поведения.
Безусловный вызов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; };