2012-07-12 12 views
12
#include <iostream> 
struct B1 
{ 
    virtual void method()=0; 
    virtual ~B1(){} 
}; 

struct B2 
{ 
    virtual void method()=0; 
    virtual ~B2(){} 
}; 

struct D: B1, B2 
{ 
    virtual void method() 
    { 
     std::cout << "D::method\n"; 
    }; 
}; 

int main(int argc,char *argv[]) 
{ 
    D d; 
    B1 &b1=d; 
    B2 &b2=d; 
    b1.method(); 
    b2.method(); 
    return 0; 
} 

Uwaga, B1 i B2 nie mają wspólnego interfejsu.Zastępowanie funkcji wirtualnej klas podstawowych, które nie mają wspólnego interfejsu.

Czy to jest legalne? Jeśli tak - w jakim standardzie? C++ 98/03/11?

Zarówno msvc, jak i gcc skompilowały go poprawnie.

Wcześniej myślałem, że muszę użyć jakiegoś wspólnego interfejsu dla takiego przypadku (możliwe wirtualne dziedziczenie).

Czy taka sytuacja ma jakąś specjalną nazwę?

Jak to działa w szczegółach, proszę? Może jakieś referencje ISO?

+0

Jeśli wyjaśnisz, co prowadzi do tego, że jest to specjalny przypadek, ktoś może znaleźć dobre wyjaśnienie. –

+0

Ponieważ w takim przypadku oczekuję dwóch różnych * dziedziczonych * dziedziczonych funkcji wirtualnych w metodzie D: B1 :: i B2 ::. – John

Odpowiedz

7

Kod jest dobrze uformowany: void D::method() przesłonięcia zarówno void B1::method() i void B2::method().

W specyfikacji podano (C++ 11 §10.3/2)

Jeżeli wirtualne funkcja element vf deklaruje klasą Base w klasie Derived, pochodzący, bezpośrednio lub pośrednio, z Base, funkcja element vf o tej samej nazwie, parametr typu lista- , kwalifikacja cv i ref-kwalifikator (lub brak tego samego), co deklarowany jest Base::vf, następnie Derived::vf jest również wirtualny (niezależnie od tego, czy jest tak zadeklarowany) i przesłania Base::vf.

B1 deklaruje wirtualną funkcję członka void B1::method(). Klasa D wywodzi się z B1 i deklaruje również funkcję członka o tej samej nazwie (method), tę samą listę parametrów (bez parametrów), taką samą kwalifikację cv (bez kwalifikacji) i ten sam kwalifikator referencji (brak kwalifikacji).

Dlatego void D::method() zastępuje void B1::method().

Ta sama logika dotyczy void B2::method() (wystarczy zastąpić B2 dla B1 w powyższym wyjaśnieniem), więc void D::method() przesłonięcia zarówno void B1::method() i void B2::method().

+0

Dziękuję, to brzmi jak prawdziwe. Znalazłem podobne elementy w C++ 03 i C++ 98. Ale jestem ciekawy, czy są jakieś dodatki w tej sytuacji? Może jakieś specjalne uwagi dotyczące dziedziczenia wielokrotnego? – John

+0

Oprócz "i ref-qualifier" oczekiwałbym, że specyfikacja będzie taka sama w C++ 98 i C++ 03. –

+0

Świetnie, dobrze wiedzieć. By the way, just * using * method() * bez * przesłonięcia jest dozwolone tylko dla D, jeśli D jest uzyskiwany poprzez referencje/wskaźniki klasy bazowej. Porównaj http://stackoverflow.com/questions/3310910/method-resolution-order-in-c/3310948#3310948 –

1

afaik jest to zgodne z prawem w każdym standarcie. Nie jestem pewien, czy ma on swoją własną nazwę, ale jest podobny do diamond problem.

jeśli zastąpisz "wirtualną metodę void()" w D, to przesłonisz obie metody w B1 i B2.

Edit:

do anwser dlaczego nie ma "dwa różne w niezależnie dziedziczone wirtualne funkcje w D: B1 i B2 :: metoda :: Metoda":

gdy przesłanianie metody, ty może tylko określać nazwę funkcji, typ i parametry zwracane, ale nie można dodać informacji o podpisie, którego dziedziczy po nadpisaniu.

wyobrazić, że byłoby to możliwe, może to wyglądać mniej więcej tak:

struct D: B1, B2 
{ 
    virtual void B1::method() 
    { 
     std::cout << "D::method\n"; 
    }; 
    virtual void B2::method() 
    { 
     std::cout << "D::method\n"; 
    }; 
}; 

ale widząc to można już powiedzieć, że nie ma możliwości, aby mieć coś takiego, ponieważ podczas rozmowy

objectD.method() 

Nie możesz określić, do którego dzwonisz. więc nawet gdyby istniał sposób na przeciążenie obu, nadal istnieje problem odróżnienia w wywołaniu funkcji.

EDYCJA: "Nie można określić, do którego dzwonisz." odnosi się do, nie możesz określić, czy chcesz wywołać przeciążenie klasy D metody B2 ::, czy samą metodę B2. Metoda objectD.B2 :: metoda będzie zawsze wywołać B2 (nie przeciążony) (który w tym przypadku nie będzie skompilować jako B2 ma realizacji)

+0

Dziękuję, zrozumiałem to. Chciałbym poznać więcej szczegółów, ponieważ jest to dla mnie trochę zaskakujące. – John

+0

@James McNellis nie byłoby to możliwe tylko wewnątrz członka klasy? void B1method() {B1 :: method}? – cppanda

+1

sprawdź to http://ideone.com/jdsMI – John