web-dev-qa-db-fra.com

Comment utiliser unique_ptr pour pimpl?

Voici une simplification de ce que je vois lorsque j'essaie d'utiliser unique_ptr pour pimpl. J'ai choisi unique_ptr parce que je veux vraiment que la classe soit propriétaire du pointeur - je veux que les durées de vie du pointeur pimpl et de la classe soient identiques.

Quoi qu'il en soit, voici l'en-tête:

#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

Voici la source:

#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}}
{ }

Je pourrais bien les compiler dans une bibliothèque. Mais quand j'essaie de l'utiliser dans un programme de test, je reçois

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'

Ceci est bien connu dispositif de sécurité . J'ai essayé de suivre.

Mon problème est que si je mets la déclaration Help :: Impl dans un en-tête, cela semblerait éviter tout avantage de pimpl. La disposition des classes est visible pour les utilisateurs. La définition est cachée mais j'aurais pu le faire avec la classe Aide et les membres privés. De plus, l'inclusion de la déclaration d'Impl apporte de nouveaux en-têtes que j'aurais aimé garder séparés.

Qu'est-ce que je rate? Que mettent les gens dans une déclaration Impl et où? Suis-je en train de mal faire l'aide? Argh!

60
emsr

Je crois que votre test_help.cpp voit en fait le destructeur ~Help() que vous avez déclaré par défaut. Dans ce destructeur, le compilateur essaie de générer le unique_ptr destructor aussi, mais il a besoin de la déclaration Impl pour cela.

Donc, si vous déplacez la définition du destructeur vers Help.cpp, ce problème devrait avoir disparu.

- EDIT - Vous pouvez également définir le destructeur comme valeur par défaut dans le fichier cpp:

Help::~Help() = default;
79
xtofl

Notez cela dans la définition nique_ptr :

std :: unique_ptr peut être construit pour un type T incomplet, de manière à faciliter l'utilisation en tant que descripteur dans l'idiome pImpl. Si le suppresseur par défaut est utilisé, T doit être complet au point de code où le suppresseur est appelé, ce qui se produit dans le destructeur, déplacer l'opérateur d'affectation et réinitialiser la fonction membre de std :: unique_ptr.

8
parasrish