2012-04-16 7 views
6

andere verweis so questionVerwirrung in Bezug auf Namen versteckt und virtuelle Funktionen

den Code vor:

class Base { 
public: 
    virtual void gogo(int a){ 
     printf(" Base :: gogo (int) \n"); 
    }; 

    virtual void gogo(int* a){ 
     printf(" Base :: gogo (int*) \n"); 
    }; 
}; 

class Derived : public Base{ 
public: 
    virtual void gogo(int* a){ 
     printf(" Derived :: gogo (int*) \n"); 
    }; 
}; 

int main(){ 

    // 1)  
    Derived * obj = new Derived ; 
    obj->gogo(7); // this is illegal because of name hiding 


    // 2)  
    Base* obj = new Derived ; 
    obj->gogo(7); // this is legal 
} 

Für Fall 2)

Der Anruf obj->gogo(7) wird zur Laufzeit aufgelöst.

Seit obj->gogo(7) ist legal. Es scheint, dass VTable von Derived enthält ptr zu virtual void gogo(int a) zu implizieren, die versteckt werden sollen.

Meine Verwirrung, da Namen versteckt verursacht Fall 1) illegal sein, so wie der Anruf in 2) während der Laufzeit aufgelöst wird

a) Ist VTable von Abgeleitet enthält Zeiger auf gogo (int).

b) Wenn a) nicht wahr ist, wird für virtuelle Funktionen Call Resolution geht von Basisklasse Vtable.

+0

@AndersK Die Funktion 'Base :: gogo (int)' ist in der Tat versteckt durch 'Derived :: gogo (int *)'. Aber eine Anweisung "using Base :: gogo;" in der Klasse "Derived" würde dieses spezielle Problem lösen. –

+0

@MichaelWild Ja, ich habe meinen Fehler gesehen. –

Antwort

0

Da Sie die zweite obj als Base* deklariert haben, gibt die VTable alle Methoden von Base. Obwohl für virtuelle Methoden, die von Derived außer Kraft gesetzt wurden, werden die überschriebenen Versionen noch diejenigen genannt werden, sind die anderen Verfahren (oder Verfahren Überlastungen), die in Base erklärt wurden.

Wenn Sie jedoch den Zeiger als Derived* erklären, wird die V-Tabelle gibt die Methoden des Derived, die, die versteckt, die die gleichen Namen in Base hatte. Daher wird obj->gogo(7); nicht funktionieren. In ähnlicher Weise ist dies auch illegal:

Base* obj = new Derived(); 

// legal, since obj is a pointer to Base, it contains the gogo(int) method. 
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden. 
(reinterpret_cast<Derived*>(obj))->gogo(7); 

Diese legal ist:

Derived* obj = new Derived ; 
obj->Base::gogo(7); // legal. 

here See.

+0

Wäre 'dynamic_cast' in Ihrem Beispiel nicht passender? – Griwes

+0

Soweit ich weiß, hat das nichts mit der eigentlichen 'vtable'-Suche zu tun, sondern mit statischer Typisierung und dem Ausblenden von Namen (siehe Bo Persons Antwort). Wenn der Typ "Abgeleitet" ist, gelten diese Semantiken, wenn der Typ "Basis" ist, gilt eine andere Semantik. – KillianDS

5

Sie sind verwirrend virtuelle Funktionen Anrufe und Überladungsauflösung.

Alle abgeleiteten Klassen haben vtables alle virtuellen Funktionen enthält, von der Basisklasse und alle zusätzlichen eigenen virtuellen Funktionen. Dies wird verwendet, um Anrufe bei Laufzeit aufzulösen, wie in Ihrem Fall 2).

Im Fall 1) einen Fehler vor Überlastung Auflösung bei Kompilierung bekommen. Aufgrund des Namensverbergens hat die Klasse Derived nur eine aufrufbare Funktion. Ihre einzige Wahl ist, diese Funktion mit einem int* aufzurufen.

+0

Nein, es gibt keinen Fehler. Da beide 'gogo()' Überladungen in 'Base' deklariert wurden, wird das Überschreiben des anderen nicht ausgeblendet. –

+0

Dies erklärt das obige Verhalten. Der C++ Standard (oder irgendein Buch/Dokument) beschreibt dasselbe. Ich meine, ist es möglich, ein Zitat zu bekommen. – fizzbuzz

+0

@fizzbuzz - Ein in einem inneren Bereich deklarierter Name verbirgt immer einen Namen in einem äußeren Bereich. In diesem Fall ist die abgeleitete Klasse der innere Bereich und die Basisklasse der äußere Bereich. –

1

Im Grunde tritt Funktionsüberladung nur dann auf, wenn die gleichnamigen Funktionen im selben Bereich definiert sind. Nun hat die Basisklasse ihren eigenen Gültigkeitsbereich und die abgeleitete Klasse hat ihren eigenen Gültigkeitsbereich.

Wenn Sie also eine Funktion in der abgeleiteten Klasse nicht neu definieren und diese Funktion aufrufen, überprüft der Compiler den Gültigkeitsbereich von abgeleitet, entdeckt, dass dort keine solche Funktion definiert ist. Dann prüft es den Umfang der Basisklasse, ermittelt die Funktion und bindet den Funktionsaufruf entsprechend an diese bestimmte Definition.

Wenn Sie jedoch die Funktion mit unterschiedlicher Signatur neu definieren, vergleicht der Compiler den Aufruf mit dieser Funktion, sieht Inkonsistenzen und klagt einfach.

Sie können dieses Verhalten ändern, indem Sie in der abgeleiteten Klassenverteidigung "using Base :: gogo;" hinzufügen. Ich hoffe das erklärt.

2

Sie möchten die überladene Funktion überschreiben, aber die Regeln zum Ausblenden funktionieren nicht so.

"Die Versteckregel besagt, dass eine Entität in einem inneren Bereich Objekte mit demselben Namen in einem äußeren Bereich versteckt."

Hinweis, es ist irrelevant, dass es unterschiedliche Signatur hat, d. H. gogo(int* a) wird alle gogo(whatever) Funktionen von der Basis ausblenden. Das Überschreiben tritt nur auf, wenn Ihre Funktionen denselben Namen, dieselben Signaturen und virtuelle Namen haben (so wird nur gogo(int* a) überschrieben).

Die C++ - FAQ-Buch schlagen vor, "nicht-virtuelle Überladungen, die nicht überladene virtuals aufrufen." (Kapitel 29.05). Grundsätzlich Sie schaffen nicht-virtuellen ladenen Funktionen in der Basisklasse:

gogo(int a) and gogo(int* a)

die virtuellen Funktionen aufrufen werden jeweils:

virtuellen gogo_i (int a) und virtuellen gogo_pi (int * a)

Und überschreiben Sie diese virtuals in abgeleiteten Klasse.

Verwandte Themen