Il y a un problème très réel avec les bibliothèques partagées que l'idiome pimpl contourne parfaitement que les virtuels purs ne peuvent pas: vous ne pouvez pas modifier / supprimer en toute sécurité les membres de données d'une classe sans forcer les utilisateurs de la classe à recompiler leur code. Cela peut être acceptable dans certaines circonstances, mais pas par exemple pour les bibliothèques système.
Pour expliquer le problème en détail, considérez le code suivant dans votre bibliothèque / en-tête partagé:
// header
struct A
{
public:
A();
// more public interface, some of which uses the int below
private:
int a;
};
// library
A::A()
: a(0)
{}
Le compilateur émet du code dans la bibliothèque partagée qui calcule l'adresse de l'entier à initialiser pour être un certain décalage (probablement zéro dans ce cas, car c'est le seul membre) du pointeur vers l'objet A qu'il sait être this
.
Du côté utilisateur du code, a new A
allouera d'abord des sizeof(A)
octets de mémoire, puis remettra un pointeur vers cette mémoire au A::A()
constructeur sous la forme this
.
Si dans une révision ultérieure de votre bibliothèque vous décidez de supprimer l'entier, de le rendre plus grand, plus petit ou d'ajouter des membres, il y aura un décalage entre la quantité de mémoire allouée par le code de l'utilisateur et les décalages attendus par le code du constructeur. Le résultat probable est un crash, si vous avez de la chance - si vous êtes moins chanceux, votre logiciel se comporte bizarrement.
En pimpl'ing, vous pouvez ajouter et supprimer en toute sécurité des membres de données à la classe interne, car l'allocation de mémoire et l'appel du constructeur se produisent dans la bibliothèque partagée:
// header
struct A
{
public:
A();
// more public interface, all of which delegates to the impl
private:
void * impl;
};
// library
A::A()
: impl(new A_impl())
{}
Tout ce que vous avez à faire maintenant est de garder votre interface publique exempte de membres de données autres que le pointeur vers l'objet d'implémentation, et vous êtes à l'abri de cette classe d'erreurs.
Edit: Je devrais peut-être ajouter que la seule raison pour laquelle je parle du constructeur ici est que je ne voulais pas fournir plus de code - la même argumentation s'applique à toutes les fonctions qui accèdent aux données membres.