Сценарий Diamond-inheritance прекрасно компилируется в g++, но создает предупреждения/ошибки в VС++/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'
.
При компиляции с визуальной студией я получаю 2 предупреждения:
warning C4250: 'DerivedC' : inherits 'DerivedB::DerivedB::B' via dominance
warning C4250: 'DerivedC' : inherits 'DerivedA::DerivedA::A' via dominance
Итак, вопрос:, как правильно это сделать? Вызывает ли код выше поведение undefined?
Примечание: Название может быть немного вводить в заблуждение, я не знаю, какой хороший заголовок для этого вопроса будет.
Ответы
Ответ 1
Каков правильный способ сделать это? Вызывает ли код выше поведение undefined?
Код отлично действует. Здесь нет undefined Поведение.
Неквалифицированный вызов A()
через объект класса DerivedC
всегда будет вызывать DerivedA::A()
, а неквалифицированный вызов B()
через объект класса DerivedC
всегда будет вызывать экземпляр DerivedB::B()
.
Visual С++ дает вам предупреждение, потому что ваш код использует менее известную функцию виртуального наследования, которая может быть не очевидна для большинства обычных пользователей и может удивить их. В этом случае предупреждение должно быть принято как информативный нитпик, а не предупреждение.
Обратите внимание, что стандарт С++ не ограничивает компиляторы от выдачи информационных предупреждений для совершенно правильного кода.
В документации для предупреждения C4250 приведен пример, который говорит вам, почему Visual С++ решает дать это предупреждение.
Ответ 2
Возможно, вы захотите попробовать следующее:
class DerivedC: public DerivedA, public DerivedB {
public:
using DerivedA::A;
using DerivedB::B;
};
Я не могу тестировать сам Eclipse или VС++...
Ответ 3
Я не знаю, почему компилятор будет жаловаться на все это; это
просто стандартная техника mixin. Классы Base
, DerivedA
и
DerivedB
являются абстрактными и не могут быть созданы, но обычно
случай для миксинов. Весь смысл микса заключается в том, что он не
реализовать весь интерфейс. И DerivedC
реализует как A()
и B()
через свои унаследованные элементы.
Если компилятор отказывается компилировать этот код, он прерывается.
Что касается предупреждений... компилятор может предупредить обо всем, что угодно:
-
Нет требования, чтобы класс с виртуальными членами
виртуальный деструктор. На практике это обычно хорошая идея, однако
(если деструктор не защищен), а предупреждение компилятора
необходимо.
-
Предупреждения из Visual Studio являются "информативными", я думаю, но это
это способ, которым язык предназначен для работы. И это, конечно, не
что-то избегать. В этом отношении я не думаю, что господство
на самом деле играет здесь роль, поскольку функции из Base
являются чистыми
виртуальная. Кажется, что Visual Studios пытается сказать, что в
DerivedC
, фактическая перегрузка A()
равна DerivedA::A()
, а не
Base::A()
. Кажется, это то, что интуитивно ожидало бы от меня;
правила, касающиеся доминирования, на самом деле являются лишь формальным утверждением
что интуитивно ожидало бы.
Во всяком случае, я бы определенно отказался от предупреждения о доминировании. Там в
в этом отношении не о чем беспокоиться. И я бы пожаловался
громко о компиляторе, который не скомпилировал код.
Ответ 4
Известно, что 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.
Ответ 5
Ваш базовый класс является абстрактным: он не может быть создан.
Класс 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;
};