2010-06-17 23 views
15

Por favor, ver el código de ejemplo a continuación:La función de miembro de clase anidada no puede acceder a la función de clase adjunta. ¿Por qué?

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

Dentro de la clase A & aplicación B:

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

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

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

Las opciones del compilador la llamada a foo() en el método de foobar(). Anteriormente, tenía foo() como función de miembro privado de la clase A, pero cambió a público suponiendo que la función de B no puede verlo. Por supuesto, no ayudó. Estoy tratando de volver a utilizar la funcionalidad proporcionada por el método de A. ¿Por qué el compilador no permite esta llamada de función? Como lo veo, son parte de la misma clase adjunta (A). Pensé que se había resuelto el problema de accesibilidad para los miembros de clase anidados para encerrar clase en estándares de C++.

¿Cómo puedo lograr lo que estoy tratando de hacer sin volver a escribir el mismo método (foo()) para B, que mantiene B anidado dentro de A?

Estoy usando el compilador VC++ ver-9 (Visual Studio 2008). Gracias por tu ayuda.

Respuesta

14

foo() es una función miembro no estática de A e intenta llamar sin una instancia.
La clase anidada B es una clase separada que solo tiene algunos privilegios de acceso y no tiene ningún conocimiento especial sobre las instancias existentes de A.

Si B necesita tener acceso a una A hay que darle una referencia a él, por ejemplo .:

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

+1, solo un nitpick - 'parent' probablemente no sea el mejor nombre para la variable miembro aquí - confusión fácil con la herencia. –

+1

Solo quería mencionar que había una muy buena razón de diseño para que la clase anidada B tuviera una referencia a la clase A. – manifest

+0

Gracias por la explicación con el ejemplo. El estándar C++ 11.8, que estaba adivinando que había cambiado, habla sobre el acceso de miembro de clase por clase anidada. Sé que gcc permite el acceso por clase anidada (estoy seguro) pero el compilador de MS VC no. Mmm interesante. – Rahul

0

Si desea reutilizar la funcionalidad de A, entonces debe heredar de A y no anidar B en su interior.

+1

No estoy extendiendo, EL FUNCIONAMIENTO de A en B, así, no hay necesidad para heredar B de A. Además, me gustaría mantener B oculto para los usuarios de la clase A. – Rahul

1

Esta es una automagic, aunque posiblemente no portátil truco (trabajado en VC++ 6.0 ya sin embargo). La clase B debe ser miembro de la clase A para que esto funcione.

#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

Gracias, Igor por responder a mi pregunta y dar un ejemplo. Usted y Georg tienen una implementación casi similar con una referencia de regreso a la clase externa, sin embargo, he seleccionado la respuesta de Georg ya que es más limpia. – Rahul

+0

No te preocupes, después de todo esto es un truco bastante desagradable. Habiendo dicho eso, ha sido bastante confiable y es un poco divertido :) –

0

Básicamente lo que dijo Georg Fritzsche

#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 se quite el constructor por defecto B que se obtendría un error

+0

No recibí el error. – cbinder

+0

@cbinder try uncommenting 'A :: B :: B() {};' también – enthusiasticgeek

Cuestiones relacionadas