2016-12-09 88 views
25
class A {}; 
class B : private A {}; 
class C : private B 
{ 
public: 
    class D : private A {}; // Error here 
}; 

Ten kod daje następujący błąd (w VS 2013):Błąd zagnieżdżone klasy dziedziczenia

nested.cpp (8): error C2247: 'A' nie jest dostępna, ponieważ 'B' używa ' prywatne”odziedziczyć od«A»

robi stałe jeśli zmienić definicję D takiego:

class D : private ::A {}; 

Czy to prawidłowe zachowanie, a jeśli tak, dlaczego?

Początkowo myślałem, że to dlatego, że C dziedziczy prywatnie od B, który ukryłby klasy bazowe. Ale jeśli wyeliminować „człowiek środka” klasy B i po prostu korzystać z tego:

class A {}; 
class C : private A 
{ 
public: 
    class D : private A {}; 
}; 

Błąd odchodzi.

+1

Kompilator mówi, dlaczego. * ponieważ 'B' używa 'private' do dziedziczenia z 'A' *, nie dlatego, że C dziedziczy prywatnie z B. –

+0

@ n.m. Cóż, to niewiele mi mówi. W końcu używam klasy z globalnej przestrzeni nazw, a nie członka czegoś. W każdym razie dostaję to teraz dzięki odpowiedziom. – user1610015

+0

Nazwy są wyszukiwane od wewnątrz. Po znalezieniu nazwy wyszukiwanie zostaje zatrzymane. Następnie sprawdzana jest dostępność. W twoim przypadku ścieżka wyszukiwania to C> B> A. B> A jest zablokowany z powodu prywatnego dziedziczenia, A jest niedostępny. Oto, co mówi kompilator. To, że A istnieje również w zakresie globalnym i jest takie samo A, nie ma znaczenia. –

Odpowiedz

18

Cytat cppreference:

nazwę, która jest prywatna według nazwy niewykwalifikowanego odnośnika może być dostępny poprzez kwalifikowana nazwa odnośnika

Mając to na uwadze, pozwala zobaczyć, jak niekwalifikowane Wyszukiwanie nazw zadziała w pierwszym przykładzie:

class A {}; 

class B : private A {}; 

class C : private B 
{ 
public: 
    class D : private A {}; // Error here 
}; 
  1. A zostanie wyświetlony w zakresie C. Gdyby został tam zdefiniowany, nie byłoby problemu.
  2. Wynika z tego, że A jest prywatnie dziedziczony przez jego bazę (prywatna) klasa B i dlatego generuje błąd kompilatora. Clang mówi:
     
    note: constrained by private inheritance here: 
    class B : private A {};

Ponownie, jak w cytacie to powinno działać, jeśli używać pełnej nazwy, tak jak wykazały

class D : private ::A {}; 

A co do ostatniego przykładu:

class A {}; 

class C : private A 
{ 
public: 
    class D : private A {}; 
}; 

Działa, ponieważ wyszukiwanie nazw działa dla wszystkich nazw należących do tej samej klasy. Zacytować cppreference ponownie:

Wszyscy członkowie klasy (organy funkcji składowych, inicjalizatory z obiektów członkowskich, a cały definicji zagnieżdżone klasy) mają dostęp do wszystkich nazw, do których klasa ma dostęp.

+0

Dzięki, że naprawdę to wyjaśniłeś (i skypjack też). – user1610015

+0

_Prawdopodobnie liczy się out_? Odpowiedź powinna być odpowiedzią, a nie innym podstawowym pytaniem. – skypjack

13

To kwestia zakresów podczas wyszukiwania nazw:

  • Podczas korzystania ::A jest to w pełni kwalifikowana nazwa, więc jesteś wyraźnie odnosząc się do globalnej przestrzeni nazw i zbierając A stamtąd.

  • Kiedy dziedziczą A, C (powiem) widzi A i można bezpośrednio odnosić do nazwy A ciągu C z niewykwalifikowanego nazwy.

  • Kiedy dziedziczą B, CwidziB i A jest prywatny w swoim zakresie. Jest prywatny, ale istnieje. Ponieważ A jest niekwalifikowaną nazwą i jest poszukiwany przede wszystkim w tym zakresie, okazuje się, że jest ona znaleziona i niedostępna, a więc i błąd.

7

Przykład z cppreference:

class A { }; 
class B : private A { }; 
class C : public B { 
    A* p; // error: unqualified name lookup finds A as the private base of B 
    ::A* q; // OK, qualified name lookup finds the namespace-level declaration 
}; 

Z prywatnego dziedziczenia, publicznym i chronionego członka klasy bazowej stają prywatnych członków klasy pochodnej.

class B : private A {}; 

class C : private B 
{ 
public: 
    class D : private A {}; // Error because all members of A is private to B so what 
    //would be the use of this private inheritance if you can't access any of A's member. 
}; 

Podczas

class D :private ::A {}; 

prace ponieważ członkom są bezpośrednio zaczerpnięte z globalnej przestrzeni nazw, umożliwiając D mieć dostęp do członków publicznych i chronionych A „s.

2

klasa D: prywatny :: A {}; Kiedy dziedziczysz z B, C widzi, że B i A są prywatne w swoim zasięgu. Jest prywatny, ale istnieje. Ponieważ A jest bezwarunkową nazwą i jest poszukiwany przede wszystkim w tym zakresie, okazuje się, że jest i jest niedostępny, a więc i błąd.

4

Kluczowym elementem dla zrozumienia, co nie jest wyraźnie napisane w innych odpowiedzi, jest następujący:

Nazwa klasy jest wtryskiwany do zakresu klasach.

Oznacza to, że jeśli masz

class A {}; 

wtedy można odwołać się do klasy A nie tylko nazwą ::A ale także pod nazwą A::A. Zauważ, że pomimo opisywania tej samej klasy, są one o tej samej nazwie, ponieważ znajdują się w różnych zakresach.

Teraz, gdy w zakresie A lub klasy wynikających bezpośrednio lub pośrednio z A, niewykwalifikowany odnośników znajdzie A::A zamiast ::A (chyba A::A sama jest ukryty przez jeszcze inną nazwą).

Ponadto, w przeciwieństwie do niektórych innych języków, C++ nie ukryć prywatne nazwy z zakresów, gdzie nie można z nich korzystać, ale używa specyfikatorów dostępu tylko jako pozwolenie na używanie nazwy. Co więcej, te uprawnienia są powiązane z nazwą, a nie z nazwaną jednostką (w tym przypadku klasą).

Więc w kodzie, na niewykwalifikowanego odnośnika z A kompilator znajdzie nazwę C::B::A::A który ukrywa nazwę ::A, a następnie sprawdza uprawnienia dostępu i uważa, że ​​to nazwisko jest prywatny w obecnej sytuacji, ponieważ jest to nazwa w zakres C::B::A, do którego nie można uzyskać dostępu z poziomu C, ponieważ A to prywatna klasa bazowa B.