c++ - ¿Cómo uso unique_ptr para pimpl?
c++11 pimpl-idiom (2)
Aquí hay una simplificación de lo que estoy viendo cuando trato de usar unique_ptr para pimpl. Elegí unique_ptr porque realmente quiero que la clase sea dueña del puntero; quiero que la duración del puntero y la clase sean iguales.
De todos modos, aquí está el encabezado:
#ifndef HELP
#define HELP 1
#include <memory>
class Help
{
public:
Help(int ii);
~Help() = default;
private:
class Impl;
std::unique_ptr<Impl> _M_impl;
};
#endif // HELP
Aquí está la fuente:
#include "Help.h"
class Help::Impl
{
public:
Impl(int ii)
: _M_i{ii}
{ }
private:
int _M_i;
};
Help::Help(int ii)
: _M_impl{new Help::Impl{ii}}
{ }
Podría compilarlos en una biblioteca sin problemas. Pero cuando trato de usarlo en un programa de prueba consigo
ed@bad-horse:~/ext_distribution$ ../bin/bin/g++ -std=c++0x -o test_help test_help.cpp Help.cpp
In file included from /home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/memory:86:0,
from Help.h:4,
from test_help.cpp:3:
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h: In instantiation of ''void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = Help::Impl]'':
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:245:4: required from ''void std::unique_ptr<_Tp, _Dp>::reset(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>; std::unique_ptr<_Tp, _Dp>::pointer = Help::Impl*]''
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:169:32: required from ''std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = Help::Impl; _Dp = std::default_delete<Help::Impl>]''
Help.h:6:7: required from here
/home/ed/bin/lib/gcc/x86_64-unknown-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:63:14: error: invalid application of ''sizeof'' to incomplete type ''Help::Impl''
Esta es una característica de seguridad bien conocida. Intenté seguir.
Mi problema es que si coloco la declaración Help :: Impl en un encabezado, parecería obviar cualquier ventaja de pimpl. El diseño de la clase es visible para los usuarios. La definición está oculta, pero podría haberlo hecho con la clase de Ayuda y los miembros privados. Además, incluir la declaración de Impl trae nuevos encabezados que me hubiera gustado mantener separados.
¿Qué me estoy perdiendo? ¿Qué ponen las personas en una declaración Impl y dónde? ¿Estoy haciendo mal el asistente de ayuda? Argh!
Creo que tu test_help.cpp realmente ve el destructor ~Help()
que declaraste predeterminado. En ese destructor, el compilador también intenta generar el destructor unique_ptr
, pero necesita la declaración Impl
para eso.
Entonces, si mueve la definición del destructor al Help.cpp, este problema debería desaparecer.
- EDITAR - También puede definir el destructor como predeterminado en el archivo cpp:
Help::~Help() = default;
Tenga en cuenta esto de la definición de unique_ptr :
std :: unique_ptr se puede construir para un tipo T incompleto, como para facilitar el uso como manejador en el idioma de ejecución. Si se usa el eliminador predeterminado, T debe estar completo en el punto en el código donde se invoca el eliminador, lo que ocurre en el destructor, mover el operador de asignación y restablecer la función de miembro de std :: unique_ptr.