2009-09-10 10 views
20

Spodziewam się, że jeśli foo zostanie zadeklarowany w klasie D, ale nie zostanie oznaczony jako wirtualny, wówczas poniższy kod wywoła implementację foo w D (niezależnie od typu dynamicznego d).W języku C++ funkcja jest automatycznie wirtualna, jeśli zastępuje funkcję wirtualną?

D& d = ...; 
d.foo(); 

Jednak w poniższym programie tak nie jest. Czy ktoś może to wyjaśnić? Czy metoda jest automatycznie wirtualna, jeśli zastępuje funkcję wirtualną?

#include <iostream> 

using namespace std; 

class C { 
public: 
     virtual void foo() { cout << "C" << endl; } 
}; 

class D : public C { 
public: 
     void foo() { cout << "D" << endl; } 
}; 

class E : public D { 
public: 
     void foo() { cout << "E" << endl; } 
}; 

int main(int argc, char **argv) 
{ 
     E& e = *new E; 
     D& d = *static_cast<D*>(&e); 
     d.foo(); 
     return 0; 
} 

Wyjście powyższego programu jest:

E 
+4

static_cast jest zbędny - 'D-D = * static_cast (&e);' odpowiada 'D & D = E;' powodu utajonego obsady z e */E i do D */D &. –

+0

W C++ 11 dodanie "nadpisania" do deklaracji funkcji powoduje, że jasne jest, że chcesz zastąpić funkcję klasy podstawowej, a także uruchamia błąd z kompilatora, na wypadek gdyby zadeklarowana funkcja różniła się od bazy (coś, co może Zaskakuj cię, gdybyś wywodził się ze std :: exception na przykład i zadeklarował co() nie jest const) – Ghita

Odpowiedz

22

standardowa 10.3.2 (class.virtual) mówi:

Jeśli wirtualny funkcja członek VF jest zadeklarowana w klasie bazowej i w klasie pochodnej, pochodzące bezpośrednio lub pośrednio z bazy, VF funkcji członka o tej samej nazwie i tej samej liście parametrów, co deklaracja Base :: vf, a następnie Derived :: vf jest również wirtualne (niezależnie od tego, czy jest tak zadeklarowane) i przesłoniło *

[Przypis: funkcja o tej samej nazwie ale inna lista parametrów (klauzula powyżej) jako funkcja wirtualna nie musi być wirtualna i nie zastępuje. Użycie wirtualnego specyfikatora w deklaracji funkcji nadrzędnej jest legalne, ale nadmiarowe (ma pustą semantykę). Kontrola dostępu (klauzula class.access) nie jest brana pod uwagę przy ustalaniu nadpisywania. --- koniec foonote]

17

Szybka odpowiedź może nie być, ale prawidłowa odpowiedź brzmi tak

C++ nie wie o funkcji ukrycia, więc nadrzędne funkcja wirtualna bez wirtualnych znaków słownych, które również działają wirtualnie.

+11

@Yossarian, dlatego uważamy, że dobrą praktyką jest deklarowanie funkcji wirtualnych w klasach pochodnych jako wirtualnych, tak aby intencje były jasne. omawia to w swojej efektywnej książce C++. –

+0

Słyszałem gdzieś, że nie oznaczanie wirtualnych w metodach pochodnych uczyniłoby je praktycznie ostatecznymi. co sprawia, że ​​pochodna trzeciego poziomu jest nadpisaniem innym niż wirtualne. ale uważam, że ekwiwalent standardu Tadeusza dowodzi, że ta wiara jest błędna z powodu słowa "pośrednio", nieprawdaż? –

+2

@ v.oddou "Słyszałem gdzieś", że rzeczy, które ludzie "gdzieś słyszeli" są zwykle fałszywe. ;-) Dopóki istnieje funkcja z tą samą sygnaturą oznaczoną 'virtual' _somherehere_ wyżej w hierarchii, wtedy każda pochodna funkcja z tą samą sygnaturą jest nadpisywana. "Luki" w tym, czy deklaracja zawierała "wirtualne", nie mają znaczenia, ponieważ słowo kluczowe ma pustą semantykę po najbardziej podstawowym wyglądzie. –

0

Nie tworzysz żadnej kopii przedmiotu ei umieszczasz go w d. Tak więc d.foo() podąża za normalnym zachowaniem polimorficznym i wywołuje metodę klasy pochodnej. Metoda zadeklarowana jako wirtualna w klasie bazowej również staje się automatycznie wirtualna również w klasie pochodnej.

-1

Dane wyjściowe ("E") zachowują się dokładnie tak, jak można by się spodziewać.

Powód: Powodem: Typ dynamiczny (tj. Środowisko uruchomieniowe) tego odwołania to E. Wykonujesz statyczne upcast do D, ale to nie zmienia faktycznego typu obiektu.

To jest właśnie idea metod wirtualnych i dynamicznych wywołań: Widzisz zachowanie typu, który tworzyłeś, czyli E, w tym przypadku.

Powiązane problemy