2008-09-16 15 views
35

Gegeben das folgende Beispiel, warum muss ich explizit die Anweisung b->A::DoSomething() statt nur b->DoSomething() verwenden?C++ - Überladungsauflösung

Sollte die Überladungsauflösung des Compilers nicht herausfinden, über welche Methode ich spreche?

Ich verwende Microsoft VS 2005 (Hinweis: virtuelle Anwendung der in diesem Fall nicht helfen.)

class A 
{ 
    public: 
    int DoSomething() {return 0;}; 
}; 

class B : public A 
{ 
    public: 
    int DoSomething(int x) {return 1;}; 
}; 

int main() 
{ 
    B* b = new B(); 
    b->A::DoSomething(); //Why this? 
    //b->DoSomething(); //Why not this? (Gives compiler error.) 
    delete b; 
    return 0; 
} 
+0

Ich versuche, DoSomething() in Klasse A mit einem Zeiger auf Klasse B aufzurufen. – Abe

Antwort

40

Die beiden „Überlast“ sind nicht im gleichen Umfang. Standardmäßig berücksichtigt der Compiler nur den kleinstmöglichen Namensbereich, bis eine Namensübereinstimmung gefunden wird. Der Argumentvergleich ist danach. In Ihrem Fall bedeutet dies, dass der Compiler B::DoSomething sieht. Es versucht dann, die Argumentliste abzugleichen, die fehlschlägt.

Eine Lösung wäre, um die Überlastung von A in B ‚nach unten ziehen Umfang s:

class B : public A { 
public: 
    using A::DoSomething; 
    // … 
} 
+1

In der Tat, wenn sie nicht im gleichen Umfang sind, werden sie auch Überladungen genannt – Chubsdad

+3

@Chubsdad: Deshalb lege ich das Wort in Anführungszeichen. Und sobald die Methode in den gleichen Bereich gezogen wird, wird es zu einer Überlastung. –

13

Überlastung Auflösung ist eines der hässlichsten Teile von C++

Grundsätzlich ist der Compiler findet einen Namen match" DoSomething (int) "im Bereich von B, sieht die Parameter nicht übereinstimmen und stoppt mit einem Fehler.

Es kann mithilfe der A :: DoSomething in der Klasse B

class A 
{ 
    public: 
    int DoSomething() {return 0;} 
}; 

class B : public A 
{ 
    public: 
    using A::DoSomething; 
    int DoSomething(int x) {return 1;} 
}; 


int main(int argc, char** argv) 
{ 
    B* b = new B(); 
    // b->A::DoSomething(); // still works, but... 
    b->DoSomething(); // works now too 
    delete b; 
    return 0; 
} 
2

überwunden werden Wenn Sie eine Funktion in einer abgeleiteten Klasse definieren dann versteckt sie alle Funktionen mit diesem Namen in der Basisklasse. Wenn die Basisklassenfunktion virtuell ist und eine kompatible Signatur aufweist, überschreibt die abgeleitete Klassenfunktion auch die Basisklassenfunktion. Dies hat jedoch keinen Einfluss auf die Sichtbarkeit.

Sie können die Funktion der Basisklasse mit einer using-Deklaration sichtbar machen:

class B : public A 
{ 
    public: 
    int DoSomething(int x) {return 1;}; 
    using A::DoSomething; 
}; 
1

Wenn der Vererbungsbaumes für die Funktion zu nutzen, C++ verwendet den Namen ohne Argumente der Suche nach oben, wenn es sich einer Definition gefunden stoppt, dann untersucht die Argumente. In dem gegebenen Beispiel, es in der Klasse B. Um stoppt in der Lage sein zu tun, was Sie nach, Klasse B sollte wie folgt definiert werden:

class B : public A 
{ 
    public: 
    using A::DoSomething; 
    int DoSomething(int x) {return 1;}; 
}; 
1

Die Funktion wird durch die Funktion mit dem gleichen Namen versteckt in die Unterklasse (aber mit einer anderen Signatur). Sie können es mithilfe der using-Anweisung einblenden, indem Sie A :: DoSomething() verwenden;

5

Nein, dieses Verhalten ist vorhanden, um sicherzustellen, dass Sie nicht fälschlicherweise von entfernten Basisklassen erben.

Um es zu umgehen, müssen Sie dem Compiler mitteilen, welche Methode Sie aufrufen möchten, indem Sie ein A :: DoSomething in die B-Klasse einfügen.

Eine schnelle und einfache Übersicht zu diesem Verhalten finden Sie unter this article.

3

Dies hat etwas damit zu tun, wie Namensauflösung funktioniert. Im Grunde finden wir zuerst den Bereich, aus dem der Name kommt, und dann sammeln wir alle Überladungen für diesen Namen in diesem Bereich.Allerdings ist der Umfang, in Ihrem Fall der Klasse B und der Klasse B, B :: DoSomething versteckt A :: doSomething:

3.3.7 Namen versteckt [basic.scope.hiding]

.. [snip] ...

3 In einer Memberfunktionsdefinition verbirgt die Deklaration eines lokalen Namens die Deklaration eines Members der Klasse mit demselben Namen; siehe basic.scope.class. Die Deklaration eines Members in einer abgeleiteten Klasse (class.derived) verbirgt die Deklaration eines Elements einer Basisklasse desselben Namens; siehe class.member.lookup.

Wegen Namen versteckt, A :: DoSomething ist auch nicht für die Überladungsauflösung betrachtet

+0

Die tatsächliche Spezifikationsreferenz ist gut. – Sam

2

das ist eine Überlastung nicht! Das ist VERSTECKEND!

5

Das Vorhandensein einer Methode in einer abgeleiteten Klasse blendet alle Methoden mit demselben Namen (unabhängig von Parametern) in Basisklassen aus. Dies geschieht, Probleme wie diese zu vermeiden:

class A {} ; 
class B :public A 
{ 
    void DoSomething(long) {...} 
} 

B b; 
b.DoSomething(1);  // calls B::DoSomething((long)1)); 

als später jemand ändert A-Klasse:

class A 
{ 
    void DoSomething(int) {...} 
} 

jetzt plötzlich:

B b; 
b.DoSomething(1);  // calls A::DoSomething(1); 

Mit anderen Worten, wenn es nicht funktioniert So könnte eine nicht verwandte Änderung in einer Klasse, die du nicht kontrollierst (A), stillschweigend beeinflussen, wie dein Code funktioniert.

+0

Sehr schöner Punkt, muss ich sagen. Die meisten Antworten, die ich gesehen habe, haben darüber gesprochen, warum ein Fehler auftritt und wie man ihn vermeidet, aber Ihr Punkt zeigt, warum diese Funktion im realen Szenario essentiell und nützlich ist! –