2011-04-21 7 views
5

Estoy tratando de usar el patrón pimpl y definir la clase de implementación en un espacio de nombre anónimo. ¿Es esto posible en C++? Mi intento fallido se describe a continuación.¿Es pimpl compatible con espacios de nombres anónimos?

¿Es posible arreglar esto sin mover la implementación a un espacio de nombre con un nombre (o el global)?

class MyCalculatorImplementation; 

class MyCalculator 
{ 
public: 
    MyCalculator(); 
    int CalculateStuff(int); 

private: 
    MyCalculatorImplementation* pimpl; 
}; 

namespace // If i omit the namespace, everything is OK 
{ 
    class MyCalculatorImplementation 
    { 
    public: 
     int Calculate(int input) 
     { 
      // Insert some complicated calculation here 
     } 

    private: 
     int state[100]; 
    }; 
} 

// error C2872: 'MyCalculatorImplementation' : ambiguous symbol 
MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation) 
{ 
} 

int MyCalculator::CalculateStuff(int x) 
{ 
    return pimpl->Calculate(x); 
} 

Respuesta

6

No, el tipo debe ser al menos declaradas antes se puede utilizar el tipo de puntero, y poniendo espacio de nombres en el anonimato en la cabecera no va a trabajar realmente. Pero ¿por qué querrías hacer eso? Si usted realmente desea ocultar la clase de implementación, que sea una clase interna privada, es decir

// .hpp 
struct Foo { 
    Foo(); 
    // ... 
private: 
    struct FooImpl; 
    boost::scoped_ptr<FooImpl> pimpl; 
}; 

// .cpp 
struct Foo::FooImpl { 
    FooImpl(); 
    // ... 
}; 

Foo::Foo() : pimpl(new FooImpl) { } 
+1

Esto es lo que he usado durante mucho tiempo, demasiado, hasta que alguien me señaló que si exporta clase 'foo', también exporta clase' Foo: : FooImpl', y eso generalmente no es lo que quiere ... –

+0

@mmutz ¿_export_ significa '' __declspec relacionado con MS (dllexport) '? Si es así, probablemente no tenga que preocuparme. – anatolyg

+0

@anatolyg: yes, o '__attribute __ ((visibility = default))' en sistemas GCC/ELF. –

1

No, usted no puede hacer eso. Tienes que reenviar-declarar la clase Pimpl:

class MyCalculatorImplementation; 

y eso declara la clase. Si luego pone la definición en el espacio de nombres sin nombre, está creando otra clase (anonymous namespace)::MyCalculatorImplementation, que no tiene nada que ver con ::MyCalculatorImplementation.

Si esto fuera cualquier otro espacio de nombres NS, que podría modificar la visión de declaración para incluir los nombres:

namespace NS { 
    class MyCalculatorImplementation; 
} 

pero el espacio de nombres sin nombre, siendo tan mágica como es, va a resolver a otra cosa cuando ese el encabezado está incluido en otras unidades de traducción (usted declararía una nueva clase cada vez que incluya ese encabezado en otra unidad de traducción).

Pero el uso del espacio de nombres anónimo no es necesario aquí: la declaración de clase puede ser pública, pero la definición, estando en el archivo de implementación, solo es visible para codificar en el archivo de implementación.

1

Si usted quiere realmente un nombre de clase hacia adelante declarada en el archivo de cabecera y la puesta en práctica de un espacio de nombres en el anonimato en el archivo de módulo, a continuación, hacer que la clase declarada una interfaz:

// header 
class MyCalculatorInterface; 

class MyCalculator{ 
    ... 
    MyCalculatorInterface* pimpl; 
}; 



//module 
class MyCalculatorInterface{ 
public: 
    virtual int Calculate(int) = 0; 
}; 

int MyCalculator::CalculateStuff(int x) 
{ 
    return pimpl->Calculate(x); 
} 

namespace { 
    class MyCalculatorImplementation: public MyCalculatorInterface { 
     ... 
    }; 
} 

// Only the ctor needs to know about MyCalculatorImplementation 
// in order to make a new one. 
MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation) 
{ 
} 
+0

Todavía contaminarás el espacio de nombres de la clase pública con la clase 'Interface' -> sin ganancia. –

2

Sí. Hay un trabajo alrededor para esto. Declare el puntero en el archivo de encabezado como vacío *, luego use un molde de reinterpretación dentro de su archivo de implementación.

Nota: Si esta es una solución deseable es otra cuestión en conjunto. Como se dice a menudo, lo dejaré como un ejercicio para el lector.

Ver una aplicación de ejemplo a continuación:

class MyCalculator 
{ 
public: 
    MyCalculator(); 
    int CalculateStuff(int); 

private: 
    void* pimpl; 
}; 

namespace // If i omit the namespace, everything is OK 
{ 
    class MyCalculatorImplementation 
    { 
    public: 
     int Calculate(int input) 
     { 
      // Insert some complicated calculation here 
     } 

    private: 
     int state[100]; 
    }; 
} 

MyCalculator::MyCalculator(): pimpl(new MyCalculatorImplementation) 
{ 
} 

MyCalaculator::~MyCalaculator() 
{ 
    // don't forget to cast back for destruction! 
    delete reinterpret_cast<MyCalculatorImplementation*>(pimpl); 
} 

int MyCalculator::CalculateStuff(int x) 
{ 
    return reinterpret_cast<MyCalculatorImplementation*>(pimpl)->Calculate(x); 
} 
+0

Creo que los pimpl clásicos no serían tan populares, si 'reinterpret_cast' en realidad fuera necesario. Esto claramente no es el camino a seguir ... – m8mble

+0

@ m8mble Para ser claros, la pregunta no era si era aconsejable; la pregunta era si era posible.Como se plantea anteriormente, definitivamente es posible, a pesar de las otras respuestas en contrario. – markshiz

+1

@ m8mbl Su pregunta de si es necesaria es otra pregunta completamente diferente. Y entonces un voto abajo aquí parece un poco demasiado. La publicación sigue siendo informativa y ofrece una solución que podría ser útil para alguien. – markshiz

Cuestiones relacionadas