Primeiro, o PImpl é geralmente usado para classes não polimórficas. E quando uma classe polimórfica tem PImpl, ela geralmente permanece polimórfica, isto é, ainda implementa interfaces e substitui os métodos virtuais da classe base e assim por diante. Portanto, a implementação mais simples do PImpl é a interface não , é uma classe simples contendo diretamente os membros!
Existem três razões para usar o PImpl:
-
Fazendo a interface binária (ABI) independente dos membros privados. É possível atualizar uma biblioteca compartilhada sem recompilar o código dependente, mas somente enquanto a interface binário permanecer a mesma. Agora quase qualquer alteração no cabeçalho, exceto para adicionar uma função não membro e adicionar uma função de membro não virtual, altera a ABI. O idioma do PImpl move a definição dos membros privados para a fonte e, assim, dissocia a ABI de sua definição. Veja Problema da interface binária frágil
-
Quando um cabeçalho é alterado, todas as fontes, inclusive ele, precisam ser recompiladas. E compilação C ++ é bastante lenta. Então, movendo as definições dos membros privados para a fonte, o idioma do PImpl reduz o tempo de compilação, pois menos dependências precisam ser puxadas no cabeçalho e reduz o tempo de compilação após as modificações, já que os dependentes não precisam ser recompilados. (ok, isso também se aplica à função interface + factory com classes de concreto ocultas).
-
Para muitas classes em exceção de C ++, a segurança é uma propriedade importante. Muitas vezes você precisa compor várias classes em uma para que, se durante a operação em mais de um membro for lançado, nenhum dos membros seja modificado ou você tenha operações que deixarão o membro em estado inconsistente se ele for acionado e você precisar que o objeto contido permaneça consistente. Nesse caso, você implementa a operação criando uma nova instância do PImpl e as troca quando a operação é bem-sucedida.
Na verdade, a interface também pode ser usada apenas para fins de implementação, mas tem as seguintes desvantagens:
-
Adicionar método não virtual não quebra a ABI, mas adiciona uma virtual. As interfaces, portanto, não permitem a adição de métodos, o PImpl faz.
-
A interface só pode ser usada por meio de ponteiro / referência, portanto, o usuário deve cuidar do gerenciamento adequado de recursos. Por outro lado, as classes que usam o PImpl ainda são tipos de valor e manipulam os recursos internamente.
-
A implementação oculta não pode ser herdada, a classe com o PImpl pode.
E, claro, a interface não ajudará na segurança de exceção. Você precisa da indireção dentro da classe para isso.