Nach dem Wortlaut Ihrer Frage zu urteilen (Sie haben das Wort "verstecken" verwendet) wissen Sie bereits, was hier vor sich geht. Das Phänomen wird "Namensverstecken" genannt. Aus irgendeinem Grund sagen die Antwortenden jedes Mal, wenn jemand eine Frage stellt, warum das Verstecken von Namen erfolgt, dass dies als "Verstecken von Namen" bezeichnet wird, und erklären, wie es funktioniert (was Sie wahrscheinlich bereits wissen), oder erklären, wie Sie es überschreiben (was Sie tun) nie gefragt), aber niemand scheint sich darum zu kümmern, die eigentliche "Warum" -Frage anzusprechen.
Die Entscheidung, die Begründung für das Ausblenden des Namens, dh warum er tatsächlich in C ++ entwickelt wurde, besteht darin, bestimmte kontraintuitive, unvorhergesehene und potenziell gefährliche Verhaltensweisen zu vermeiden, die auftreten könnten, wenn der geerbte Satz überladener Funktionen mit dem aktuellen Satz von gemischt werden könnte Überladungen in der angegebenen Klasse. Sie wissen wahrscheinlich, dass die Überlastungsauflösung in C ++ funktioniert, indem Sie die beste Funktion aus der Gruppe der Kandidaten auswählen. Dies erfolgt durch Abgleichen der Argumenttypen mit den Parametertypen. Die Übereinstimmungsregeln können manchmal kompliziert sein und oft zu Ergebnissen führen, die von einem unvorbereiteten Benutzer als unlogisch empfunden werden. Das Hinzufügen neuer Funktionen zu einer Reihe zuvor vorhandener Funktionen kann zu einer drastischen Verschiebung der Ergebnisse der Überlastungsauflösung führen.
Angenommen, die Basisklasse B
verfügt über eine Elementfunktion foo
, die einen Parameter vom Typ akzeptiert void *
, und alle aufrufenden Aufrufe foo(NULL)
werden aufgelöst B::foo(void *)
. Nehmen wir an, es gibt keinen Namen, der sich versteckt, und dies B::foo(void *)
ist in vielen verschiedenen Klassen sichtbar, die von abstammen B
. Nehmen wir jedoch an, in einem [indirekten, entfernten] Nachkommen D
der Klasse ist B
eine Funktion foo(int)
definiert. Nun, ohne Namen versteckt D
hat beide foo(void *)
und foo(int)
sichtbar und die Teilnahme an der Überladungsauflösung. Zu welcher Funktion werden die Aufrufe aufgelöst foo(NULL)
, wenn sie über ein Objekt vom Typ ausgeführt werden D
? Sie werden sich auflösen D::foo(int)
, da dies int
eine bessere Übereinstimmung für das Integral Null ist (dhNULL
) als jeder Zeigertyp. Während der gesamten Hierarchie werden Aufrufe zur foo(NULL)
Auflösung in eine Funktion ausgeführt, während sie in D
(und unter) plötzlich in eine andere aufgelöst werden.
Ein weiteres Beispiel finden Sie in Das Design und die Entwicklung von C ++ , Seite 77:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
Ohne diese Regel würde der Status von b teilweise aktualisiert, was zu einer Aufteilung führen würde.
Dieses Verhalten wurde bei der Gestaltung der Sprache als unerwünscht angesehen. Als besserer Ansatz wurde beschlossen, die Spezifikation "Name verstecken" zu befolgen, was bedeutet, dass jede Klasse in Bezug auf jeden deklarierten Methodennamen mit einem "sauberen Blatt" beginnt. Um dieses Verhalten zu überschreiben, muss der Benutzer eine explizite Aktion ausführen: ursprünglich eine erneute Deklaration geerbter Methoden (derzeit veraltet), jetzt eine explizite Verwendung der using-Deklaration.
Wie Sie in Ihrem ursprünglichen Beitrag richtig festgestellt haben (ich beziehe mich auf die Bemerkung "Nicht polymorph"), kann dieses Verhalten als Verstoß gegen die IS-A-Beziehung zwischen den Klassen angesehen werden. Dies ist wahr, aber anscheinend wurde damals entschieden, dass sich das Verstecken von Namen am Ende als weniger böse erweisen würde.