2010-06-17 4 views
15

S'il vous plaît voir le code exemple ci-dessous:La fonction membre de la classe imbriquée ne peut pas accéder à la fonction de la classe englobante. Pourquoi?

class A 
{ 
private: 
    class B 
    { 
    public: 
     foobar(); 
    }; 
public: 
    foo(); 
    bar(); 
}; 

Dans la classe A & B mise en œuvre:

A::foo() 
{ 
    //do something 
} 

A::bar() 
{ 
    //some code 
    foo(); 
    //more code 
} 

A::B::foobar() 
{ 
    //some code 
    foo(); //<<compiler doesn't like this 
} 

Les drapeaux du compilateur l'appel à foo() dans la méthode foobar(). Auparavant, j'avais foo() en tant que fonction de membre privé de classe A mais changé en public en supposant que la fonction de B ne peut pas le voir. Bien sûr, cela n'a pas aidé. J'essaye de réutiliser la fonctionnalité fournie par la méthode de A. Pourquoi le compilateur n'autorise-t-il pas cet appel de fonction? Comme je le vois, ils font partie de la même classe englobante (A). Je pensais que le problème d'accessibilité des classes imbriquées pour l'inclusion de classe dans les normes C++ était résolu.

Comment puis-je réaliser ce que j'essaie de faire sans réécrire la même méthode (foo()) pour B, ce qui maintient B imbriqué dans A? J'utilise VC++ compiler ver-9 (Visual Studio 2008). Merci de votre aide.

Répondre

14

foo() est une fonction membre non statique de A et vous essayez de l'appeler sans instance.
La classe imbriquée B est une classe séparée qui possède uniquement certains privilèges d'accès et ne possède aucune connaissance particulière des instances existantes de A.

Si B a besoin d'accéder à un A vous devez donner une référence à elle, par exemple .:

class A { 
    class B { 
     A& parent_; 
    public: 
     B(A& parent) : parent_(parent) {} 
     void foobar() { parent_.foo(); } 
    }; 
    B b_; 
public: 
    A() : b_(*this) {} 
}; 
+3

+1, juste un nitpick - 'parent' n'est probablement pas le meilleur nom pour la variable membre ici - confusion facile avec l'héritage. –

+1

Je voulais juste mentionner qu'il y avait une très bonne raison pour que la classe B imbriquée ait une référence à la classe A. – manifest

+0

Merci pour l'explication avec l'exemple. Le standard C++ 11.8, que je devinais qu'il avait changé, parle de l'accès des membres de la classe par classe imbriquée. Je sais que gcc permet l'accès par classe imbriquée (j'en suis sûr) mais le compilateur MS VC ne le fait pas. Hum ... intéressant. – Rahul

0

Si vous souhaitez réutiliser la fonctionnalité de A, vous devez hériter de A et non de l'imbrication B qui s'y trouve.

+1

Je ne suis pas étends LE BON FONCTIONNEMENT A en B, donc, il n'y a pas besoin pour hériter B de A. Aussi, je voudrais garder B caché aux utilisateurs de la classe A. – Rahul

1

Ceci est une automagique, mais peut-être trick nonportable (travaillé sur VC++ depuis 6.0 bien). La classe B doit être membre de la classe A pour que cela fonctionne.

#ifndef OUTERCLASS 
#define OUTERCLASS(className, memberName) \ 
    reinterpret_cast<className*>(reinterpret_cast<unsigned char*>(this) - offsetof(className, memberName)) 
#endif 

class A 
{ 
private: 
    class B 
    { 
    public: 
     void foobar() { 
      A* pA = OUTERCLASS(A, m_classB); 
      pA->foo(); 
     } 
    } m_classB; 
public: 
    foo(); 
    bar(); 
}; 
+0

Merci, Igor d'avoir répondu à ma question et de donner un exemple. Vous et Georg avez une mise en œuvre presque similaire avec une référence à la classe supérieure, cependant, j'ai choisi la réponse de Georg car elle est plus propre. – Rahul

+0

Pas de soucis, c'est après tout un hack assez méchant. Cela dit, c'est assez fiable et c'est amusant :) –

0

Fondamentalement, ce que Georg Fritzsche dit

#include <iostream> 
#include <cstring> 
using namespace std; 

class A 
{ 
private: 
    class B 
    { 
    A& parent_; 
    public: 
     //B(); //uncommenting gives error 
     ~B(); 
     B(A& parent) : parent_(parent) {} 

     void foobar() 
     { 
     parent_.foo(); 
     cout << "A::B::foo()" <<endl; 
     } 

     const std::string& foobarstring(const std::string& test) const 
     { 
     parent_.foostring(test); cout << "A::B::foostring()" <<endl; 
     } 
    }; 
public: 
    void foo(); 
    void bar(); 
    const std::string& foostring(const std::string& test) const; 
    A(); 
    ~A(){}; 
    B b_; 
}; 

//A::B::B() {}; //uncommenting gives error 
A::B::~B(){}; 

A::A():b_(*this) {} 


void A::foo() 
{ 
    cout << "A::foo()" <<endl; 
} 

const std::string& A::foostring(const std::string& test) const 
{ 
    cout << test <<endl; 
    return test; 
} 

void A::bar() 
{ 
    //some code 
    cout << "A::bar()" <<endl; 
    foo(); 
    //more code 
} 

int main(int argc, char* argv[]) 
{ 
A a; 
a.b_.foobar(); 
a.b_.foobarstring("hello"); 

return 0; 
} 

Si vous supprimez le constructeur par défaut B vous obtiendrez une erreur

+0

Je n'ai pas eu l'erreur. – cbinder

+0

@cbinder essayez de ne pas ajouter de commentaire 'A :: B :: B() {};' – enthusiasticgeek

Questions connexes