2016-07-13 26 views
2

Hier ist meine Codes in der Datei source.cpp:Wie greift die Freundesklasse der Basisklasse auf Mitglieder dieser Basisklasse über Objekte der Klasse zu, die von der Basisklasse abgeleitet sind?

class B 
{ 
    friend class F; 
protected: 
    int protectedIntB; 
}; 

class D : public B {}; 

class F 
{ 
public: 
    int f(D &d) {return ++d.protectedIntB;} 
}; 

Wenn ich oben Codes mit g++ -c -Wall -pedantic -std=c++11 source.cpp und cl /c source.cpp kompilieren, kompilieren beide Compiler erfolgreich. Allerdings, wenn ich machen erbt D von B protected statt public mit:

class D : protected B {}; 

Diesmal gcc erfolgreich kompiliert, während cl ein Fehler gibt, sagt, dass B::protectedIntB in return ++d.protectedIntB; unzugänglich ist.

Eine andere Situation public mit private ersetzt:

class D : private B {}; 

Dieses Mal, ergeben beide Compiler-Fehler. Übrigens benutze ich gcc Version 5.3.0 von mingw-w64 und cl Version 19.00.24210 von VS2015.

Hier kommt meine Frage:

Wie Freund Klasse der Zugriffsbasisklasse Mitglieder dieser Basisklasse durch Objekte der Klasse von der Basisklasse abgeleitet ist, und warum gcc und cl es anders behandeln?

Edit:

Dank songyuanyao und Brian, so scheint es einen Fehler in gcc 5.3.0 im protected Fall. Nur der public Fall sollte erfolgreich kompiliert werden, und gcc 6.1.0 funktioniert auch gut.

Antwort

3

Nach [class.access.base]/5:

Der Zugang zu einem Mitglied von der Klasse betroffen ist, in der das Element gestattet. Diese Benennungsklasse ist die Klasse, in der der Mitgliedsname gesucht und gefunden wurde.

Nach [class.access.base]/6:

Wenn ein Klassenmitglied Zugang Bediener, einschließlich eines impliziten „this->“ verwendet, um ein nicht-statisches Datenelement zuzugreifen oder Nicht-statische Member-Funktion, die Referenz ist schlecht gebildet, wenn der linke Operand (im Operational Case "." als Zeiger betrachtet) nicht implizit in einen Zeiger auf die Benennungsklasse des rechten Operanden konvertiert werden kann.

daher in Ihrem speziellen Fall, um d.protectedIntB zuzugreifen, die beide der folgenden muss wahr sein:

  • Sie haben Zugriff auf protectedIntB als Mitglied von B haben, da B ist die Klasse, in der der Name protectedIntB gefunden wurde. (Anmerkung: dies kann durch erneut deklariert das Element in der abgeleiteten Klasse verändert werden, unter Verwendung eines Verwendung Deklaration; in diesem Fall die abgeleitete Klasse würde steuern.)

  • Sie haben Zugriff auf B als Ausgangspunkt haben, , dh, kann D* zu B* konvertieren. Wenn B eine öffentliche Basis von ist, in Ordnung. Wenn B eine geschützte Basis von ist, wird eine Zugriffsprüfung durchgeführt, die F::f fehlschlägt, da F kein Freund von ist und keine abgeleitete Klasse von ist.

Überraschenderweise scheint es, dass GCC der Compiler ist, die in dem geschützten Fall falsch ist, aber dieser Fehler appears fixed. Beachten Sie, dass Clang eine much better diagnostic gibt.

0

Wenn der Code auf gcc 5.3.0 kompiliert und nicht auf cl kompiliert, ist die Wahrscheinlichkeit hoch, dass einer der beiden den C++ - Standard nicht strikt durchsetzt. Wenn ich eine Vermutung für geschützte und private Vererbung annehmen muss, sollten Sie einen Compilerfehler erhalten. Für den Unterschied zwischen verschiedenen Arten von Erbschaften siehe Difference between private, public, and protected inheritance für weitere Details.

Damit die Klasse F auf die privaten Mitglieder der Klasse B zugreifen kann, sollte sie wissen, dass Klasse D von Klasse B abgeleitet ist, was nur für die öffentliche Vererbung geschieht.

3

Wenn Sie erben von B mit geschützten oder privaten anstelle von öffentlichen erbt, sollte die Kompilierung fehlschlagen.

Von der Norm $11.2/5 Accessibility of base classes and base class members [class.access.base]:

Ein Mitglied m an dem Punkt R zugänglich ist, wenn in der Klasse N benannt, wenn

(5.4) eine Basisklasse B aus N besteht, die an zugänglichen R und m ist bei R zugänglich ist, wenn in der Klasse B. den Namen [Beispiel:

class B; 
class A { 
private: 
    int i; 
    friend void f(B*); 
}; 
class B : public A { }; 
void f(B* p) { 
    p->i = 1;   // OK: B* can be implicitly converted to A*, 
        // and f has access to i in A 
} 

- Ende Beispiel]

Für Ihren ersten Fall ist die Basisklasse B von D ist zugänglich F::f(), weil es öffentliche Vererbung ist. Und B::protectedIntB ist unter F::f() zugänglich, weil es ein Freund der Klasse B ist.

Wenn Sie es in geschützte oder private Vererbung ändern, ist die Basisklasse B von nicht erneut unter F::f() verfügbar, dann sollte die Kompilierung fehlschlagen. Hinweis F::f() ist kein Freund der abgeleiteten Klasse . Es bedeutet, wenn Sie es Freund der Klasse auch machen, wird die Kompilierung erfolgreich sein.

BTW: Ich habe versucht, die geschützte Vererbung mit gcc here und es schlug fehl.