2011-12-23 12 views
8

Consulte my first attempt at answering this . Me olvidé de contar toda la historia antes en un intento de simplificar las cosas. ¡Resulta que mi ejemplo funciona! Lo siento.¿Cómo hago de main un amigo de mi clase desde dentro de una biblioteca?

Toda la historia es que esta es una biblioteca que contiene una clase en un archivo y la principal en otro archivo, todo enlazado a mi biblioteca. La biblioteca proporciona la base para un Marco de Procesos, por lo que el principal está en la biblioteca y no en el proceso.

A continuación se muestra una versión reducida de lo que tengo.

pf.hpp

using namespace std; 

namespace MyNamespace 
{ 
    class ProcessManager 
    { 
    public: 
    friend int main(int argc, char** argv); 
    private: 
    void test(); 
    }; 
}; 

pf.cpp

#include "pf.h" 

namespace MyNamespace 
{ 
    ProcessManager::test() 
    { 
    cout << "My friend has accessed my member" << endl; 
    } 
}; 

pfmain.cpp

#include "pf.hpp" 

int main(int argc, char** argv) 
{ 
    ProcessManager pm; 

    pm.test(); 
} 

Tenga en cuenta que esto no funciona en la compilación de la biblioteca

Lo que lo he intentado es:

  • Mover el amigo por todo el lugar
  • de remisión amigo para uso principal ámbito global (por ejemplo, :: principales) amigo
  • decisiones y declaraciones principales utilizan ámbito global

¿Qué me falta?

Gracias!

+1

Esto es un poco fuera de tema, pero mucha gente diría que la palabra clave 'friend' nunca es una buena solución. – asveikau

+0

No es lo ideal, estoy de acuerdo, pero a veces no tiene otra alternativa buena – Jaime

+0

Puede que desee considerar otras variantes de 'main()'. Sin embargo, no estoy seguro de si están en Facebook. –

Respuesta

9

Sólo declaran la principal fuera del MyNamespace e indique el espacio de nombres global :: en amigo declaración

//in header file of ProcessManager 
//your pf.h 

int main(int argc, char** argv); 

namespace MyNamespace 
{ 
    class ProcessManager 
    { 
    public: 
    friend int ::main(int argc, char** argv); 
    private: 
    void test(); 
    }; 
}; 
+0

No veo por qué el compilador no vería el main de pfmain.cpp en mi caso, pero esto funciona. ¡Gracias! – Jaime

+0

Los intentos anteriores mostrarían el error "error: 'int main (int, char **)' debería haber sido declarado dentro de '::'", lo que debería haber sido suficiente, pero nunca hubiera esperado que el compilador no verificara el otros archivos .o para el símbolo teniendo en cuenta cómo se configuran los encabezados. – Jaime

+0

@Jaime en C++ main() debe declararse en el espacio de nombres global. Sin embargo, el nombre no está reservado y puede usarse para funciones de miembros y en espacios de nombres, entre otros lugares. –

0

Una respuesta común podría ser proporcionar una "solicitud" clase Singleton, como por ejemplo, QApplication en Qt, un reducir su main a algo así como

int main (int argc, char** argv) { 
    YourApplication app (argc, argv); 
    return app.execute(); 
} 

Entonces usted reduce su preocupación friendness a class YourApplication vs sus otras clases, y usted sabe cómo hacerlo.

+0

La clase ya es un singleton, simplemente no mostré esa parte. Sin embargo, tener un singleton no resuelve el problema. – Jaime

0

No creo que realmente quiera hacer lo que está haciendo. Esto realmente parece un truco y un problema de diseño. Si realmente desea exponer las partes internas de su clase en alguna circunstancia especializada, puede crear una clase de acceso que también esté definida dentro de su biblioteca.

Algo como esto podría funcionar (puede necesitar declaraciones adelantadas apropiados, etc. - esto es sólo un punto de partida):

class ProcessManagerAccessor 
{ 
public: 
    ProcessManagerAccessor(ProcessManager & pm) : pm_(pm) { } 

    // add public methods to expose internals 
    void test() { pm_.test(); } 

private: 
    ProcessManager & pm_; 
}; 

class ProcessManager 
{ 
public: 
    friend class ProcessManagerAccessor; 

    // ... 
}; 

// ... 

ProcessManager pm; 
ProcessManagerAccessor pma(pm); 
pma.test(); 
+0

"En general, no desea romper ninguna encapsulación por el simple hecho de realizar pruebas (o como solía decir su madre," ¡no exponga sus partes privadas! "). La mayoría de las veces, usted debería poder probar una clase ejercitando sus métodos públicos. Si hay una funcionalidad significativa que se oculta detrás del acceso privado o protegido, eso podría ser una señal de advertencia de que hay otra clase que lucha por salir. " – bobbymcr

+0

No veo esto como diferente de dar acceso principal. El problema es que mi clase es Singleton, instanciada sin params, pero necesito establecer un estado en general antes de dar control a la clase derivada de ProcessManager. No quiero que estos métodos sean accesibles para la clase derivada. – Jaime

+0

el método de "prueba" es solo un ejemplo ... en realidad no se usa para probar – Jaime

2

@parapura proporciona una solución, pero no explica por qué se primero tiene que declarar main en el alcance global.

§7.3.1.2 [namespace.memdef] p3

[...] If a friend declaration in a nonlocal class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. [...]

Así que con esto en mente, el código sería algo como esto:

namespace MyNamespace 
{ // MyNamespace is the innermost enclosing namespace 
    // 'main' from the friend declaration is treated 
    // as if it was a member of 'MyNamespace' 
    int main(int argc, char** argv); 

    class ProcessManager 
    { 
    public: 
    friend int main(int argc, char** argv); 
    private: 
    void test(); 
    }; 
}; 

Ahora Debería ser claro por qué la función global main no era su amigo.

Cuestiones relacionadas