2010-01-26 10 views
24

Betrachten Sie das folgende Snippet:Ändern Function Zugriffsmodus in Abgeleitete Klasse

struct Base 
{ 
    virtual ~Base() {} 

    virtual void Foo() const = 0; // Public 
}; 

class Child : public Base 
{ 
    virtual void Foo() const {} // Private 
}; 

int main() 
{ 
    Child child; 

    child.Foo(); // Won't work. Foo is private in this context. 

    static_cast<Base&> (child).Foo(); // Okay. Foo is public in this context. 
} 

Ist das legal C++? "Dies" ändert den Zugriffsmodus der virtuellen Funktion in der abgeleiteten Klasse.

+1

... Hat es kompiliert und ausgeführt? – AndyG

+17

@SauceMaster, das ist kein sehr guter Hinweis darauf, ob Code in C++ zulässig ist. Es gibt viele und viele C++ - Codes, die kompiliert und ausgeführt werden, aber immer noch undefiniertes Verhalten aufrufen. Ändern Sie Compiler, Compiler-Flags, Compiler-Version und Boom, es funktioniert nicht mehr. – Glen

+0

Mit Glen vereinbart. Deshalb habe ich hier gefragt. – hlx236sk

Antwort

11

Ja, das Ändern des Zugriffsmodus in abgeleiteten Klassen ist zulässig.

Dies ist ähnlich in Form, aber verschiedene in Absicht auf das Non-Virtual Interface Idiom. Einige Gründe ist here gegeben:

Der Punkt ist, dass virtuelle Funktionen existieren Anpassung zu ermöglichen; Wenn sie nicht direkt aus dem Code der abgeleiteten Klassen aufgerufen werden müssen, müssen sie auch nicht zu etwas anderem als privat gemacht werden.

Wie, warum Sie eigentlich etwas public in der Basis machen würde, aber private in abgeleitet ohne private oder protected Erbe ist mir schleierhaft.

+0

Nizza. Ich benutzte diesen Ansatz für eine Formatierungsfacette, war mir aber nicht sicher, ob dies legal war. Vielen Dank! – hlx236sk

+4

Dies steht im Gegensatz zum NVI: Die Funktion der Basisklasse ist öffentlich + virtuell, aber in NVI ist sie privat + virtuell und die öffentliche Funktion, die sie aufruft, ist nicht virtuell. –

+0

@litb, Ah, whups. Nun, ich habe den Text repariert, da ich die Basis öffentlich vermisste, abgeleitet private virtuelle. – MSN

5

Es ist vollkommen legal C++. Sie definieren einfach eine neue Methode in der Child-Klasse.

Jetzt macht es, was Sie wollen, das ist eine andere Frage. Ich glaube, dass der Zugriffsmodus nicht Teil der Methodensignatur ist, was bedeutet, dass das Aufrufen der virtuellen Foo-Methode von Base schließlich die Child's Foo-Methode aufruft.

Also hier ist die Schlussfolgerung: es ist legal C++ und es funktioniert so, wie Sie es erwarten würden.

Ich berücksichtige nicht die Zeile child.Foo();, die nicht funktionieren kann, da es keinen Zweifel gibt, dass es versucht, auf die private Foo() - Methode von Child zuzugreifen.

4

Es scheint zu kompilieren und die richtige Methode aufzurufen.

Denken Sie daran, dass Zugriffs-Spezifizierer da sind, um einem disziplinierten Programmierer zu helfen, nicht alle Versuche zu verhindern, ihn um jeden Preis zu umgehen.

In diesem speziellen Fall hat Child keine Aufgabe, die überschriebene virtuelle Funktion privat zu machen: Soll es nicht die öffentliche Schnittstelle von Base implementieren, so dass die "ist-a" -Beziehung gilt? (Wenn Sie die öffentliche Vererbung nicht verwendet haben, was "Kind ist eine Basis" bedeutet, würde Ihr Trick nicht funktionieren.)

+1

"Denken Sie daran, dass Zugriffs-Spezifizierer einem disziplinierten Programmierer helfen, nicht alle Versuche zu verhindern, ihn um jeden Preis zu umgehen." ist ein toller Punkt – austinmarton

19

Das ist legal C++, §11.6/1 sagt:

Zugriff auf den Ruf Punkt markiert ist die Art des Ausdrucks mit das Objekt zu bezeichnen, für die die Member-Funktion aufgerufen wird (B * im Beispiel oben). Der Zugriff auf die Elementfunktion in der Klasse, in der definiert wurde (D im obigen Beispiel ), ist im Allgemeinen nicht bekannt.

Wie Sie bemerkt, Child::Foo() ist also nach wie vor erreichbar über die Basisklasse, die in den meisten Fällen unerwünscht:

Child* c = new Child; 
Base* b = c; 
c->Foo(); // doesn't work, Child::Foo() is private 
b->Foo(); // works, calls Child::Foo() 

Grundsätzlich ist die Erklärung Sie im Ausdruck beziehen den Zugriffsmodus diktiert - aber virtuelle Funktionen untergraben, dass als eine andere Funktion der genannte tatsächlich aufgerufen werden kann.

+0

Also welche Implementierung von 'Foo' wird in' b-> Foo() 'ausgeführt? 'Kind :: Foo' oder' Base :: Foo'? Wenn "Child :: Foo" dies die Zugriffskontrolle in der abgeleiteten Klasse bricht. – ThomasMcLeod

+1

@ThomasMcLeod: Der Kommentar in der gleichen Zeile sagt was;) –

Verwandte Themen