2016-07-06 4 views
1

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

class BS { 
    public: 
    virtual void meth()=0; 
}; 

class BCA : public virtual BS { 
}; 

class BSS : public virtual BS { 
}; 

class BCS : public virtual BCA, public virtual BSS { 
}; 

class BI4 { 
    public: 
    void meth() {}; 
}; 

class BT4 : public virtual BI4, public virtual BSS { 
}; 

class T4 : public virtual BCS, public virtual BT4 { 
}; 

int main() { 
    T4 t4; 
}; 

Теперь проблема заключается в том, что хотя void meth() доступен в наследство графа, несмотря на этот код не будет компилироваться:

$ g++ -c t.cc -std=c++11 
t.cc: In function ‘int main()’: 
t.cc:27:6: error: cannot declare variable ‘t4’ to be of abstract type ‘T4’ 
    T4 t4; 
    ^
t.cc:23:7: note: because the following virtual functions are pure within ‘T4’: 
class T4 : public virtual BCS, public virtual BT4 { 
    ^
t.cc:3:18: note:  virtual void BS::meth() 
    virtual void meth()=0; 
       ^
t.cc:3:18: note:  virtual void BS::meth() 

Мне кажется, что BS как-то не увидит перегруженный метод meth() через цепь BS-> BCA-> BCS-> T4-> BT4-> BI4.
Но почему? Этот метод явно доступен, алгоритм линеаризации C3, используемый C++, должен иметь возможность очень четко его находить.

+0

Nitpick: вы имеете в виду «переопределенный», а не «перегруженный». – Thomas

+1

Несколько «виртуальных» отношений наследования немного переборщиваются. Вам нужно только это для под-объектов базового класса, которые в противном случае возникали бы два или более раз. –

+0

«немного сложный» o.O Это было бы не так уж плохо, если бы имена были в любом случае разумными. –

ответ

2

Есть два основных аспекта:

  • Данный класс может переопределить только функции-члены из базовых классов.
    Поскольку ваш класс BI4 не имеет BS в качестве базового класса, он не может отменить что-либо от BS.
  • Возможно, что наследует реализацию чистой виртуальной функции, определенной в виртуальном базовом классе, как и в Java, но класс, обеспечивающий эту реализацию, сам должен иметь этот виртуальный базовый класс.

Пример:

struct Base 
{ 
    virtual void foo() = 0; 
}; 

#ifdef GOOD 
    struct Impl_foo: virtual Base 
    { 
     void foo() override {} 
    }; 
#else 
    struct Impl_foo 
    { 
     virtual void foo() {} 
    }; 
#endif 

struct Abstract_derived: virtual Base 
{}; 

struct Derived 
    : Abstract_derived 
    , Impl_foo  // Java-like implementation inheritance. 
        // In C++ called "by dominance". 
{}; 

auto main() 
    -> int 
{ 
    Derived o; 
    o.foo(); 
} 

без определения символа макросъемки в GOOD, этот код не компилируется.

+0

Большое спасибо! Труба для обходного пути. :-) – peterh

2

BI4 не наследует от BS прямо или косвенно, поэтому его метод BI4::meth() полностью не связан и не может переопределить BS::meth().

Вы можете переопределять методы только из базовых классов, а не из классов «родной» или «дядя».

5

Правила языка не позволяют этого. Виртуальная функция может быть переопределена только объявлением функции с тем же именем и параметрами в производном классе. Поскольку BI4 не является производным от BS, BI4::meth не может переопределить BS::meth. Если класс наследует (прямо или косвенно) как от BS, так и от BI4, то он наследует две функции, называемые meth: один из BS, все еще абстрактный и не переопределенный, а один от BI4.