Por que deveria ser usado o idioma" PIMPL"? [duplicado]

esta pergunta já tem uma resposta aqui:

argumento:

a linguagem PIMPL (Apontador para a implementação) é uma técnica para a implementação escondida em que uma classe pública envolve uma estrutura ou classe que não pode ser vista fora da biblioteca a a classe pública faz parte.

isto esconde detalhes de implementação interna e dados do Utilizador da biblioteca.

ao implementar este idioma, porque colocaria os métodos públicos na classe pimpl e não na classe pública, uma vez que as implementações do método de classes públicas seriam compiladas na biblioteca e o utilizador só tem o ficheiro de cabeçalho?

para ilustrar, este código coloca a implementação Purr() na classe impl e envolve-a também.

porque não? implementar ronronar diretamente na classe pública?

// header file:
class Cat {
    private:
        class CatImpl;  // Not defined here
        CatImpl *cat_;  // Handle

    public:
        Cat();            // Constructor
        ~Cat();           // Destructor
        // Other operations...
        Purr();
};


// CPP file:
#include "cat.h"

class Cat::CatImpl {
    Purr();
...     // The actual implementation can be anything
};

Cat::Cat() {
    cat_ = new CatImpl;
}

Cat::~Cat() {
    delete cat_;
}

Cat::Purr(){ cat_->Purr(); }
CatImpl::Purr(){
   printf("purrrrrr");
}
Author: Peter G., 2008-09-13

11 answers

  • porque queres {[[0]} poder usar membros privados de CatImpl. Cat::Purr() não seria permitido esse acesso sem uma declaração friend.
  • Porque então não misturamos responsabilidades: um implemento de classe, um de classe para a frente.
 37
Author: Xavier Nodet, 2013-09-12 09:49:04
Acho que a maioria das pessoas se refere a isto como expressão corporal. Veja o livro de James Coplien com estilos avançados de Programação C++ e expressões idiomáticas ( Amazon link ). Também é conhecido como o Gato de Cheshire por causa da personagem de Lewis Caroll que desaparece até que apenas o sorriso permaneça.

O código de exemplo deve ser distribuído por dois conjuntos de ficheiros de código. Então só a Cat.h é o arquivo que é enviado com o produto.

CatImpl.h é incluído pela Cat.cpp e CatImpl.cpp contém a implementação para CatImpl:: Purr (). Isto não será visível para o público usando o seu produto.

Basicamente, a ideia é esconder o máximo possível da implementação de olhos curiosos. Isto é mais útil quando você tem um produto comercial que é enviado como uma série de bibliotecas que são acessadas através de uma API que o código do cliente é compilado e ligado. Fizemos isto com a reescrita do produto Ionas Orbix 3.3 em 2000.

Tal como mencionado por outros, usando sua técnica, decupla completamente a implementação da interface do objeto. Então você não terá que recompilar tudo o que usa gato se você só quer mudar a implementação do Purr().

Esta técnica é utilizada numa metodologia denominada design by contract.

 52
Author: Rob Wells, 2013-03-11 14:24:19
Para que conste, separa a implementação da interface. Isto geralmente não é muito importante em projetos de pequena dimensão. Mas, em grandes projetos e bibliotecas, ele pode ser usado para reduzir significativamente os tempos de construção.

Considere que a implementação de Cat pode incluir muitos cabeçalhos, pode envolver meta-programação template que leva tempo para compilar por si só. Por que um usuário, que só quer usar o Cat tem que incluir tudo isso? Assim, todos os os arquivos são escondidos usando o idioma pimpl (daí a declaração para a frente de CatImpl), e usando a interface não obriga o usuário a incluí-los.

Estou desenvolvendo uma biblioteca para otimização não linear (leia "lots of nasty math"), que é implementada em modelos, então a maioria do código está em cabeçalhos. Leva cerca de cinco minutos para compilar (em uma CPU multi-core decente), e apenas processar os cabeçalhos em um .cpp de outra forma vazio leva cerca de um minuto. Por isso, qualquer pessoa que use a biblioteca tem de esperar um minuto. alguns minutos de cada vez que compilam o seu código, o que torna o desenvolvimento bastante aborrecido . No entanto, ao esconder a implementação e os cabeçalhos, apenas inclui um arquivo de interface simples, que compila instantaneamente.

Não tem necessariamente nada a ver com proteger a implementação de ser copiada por outras empresas-o que provavelmente não aconteceria de qualquer forma, a menos que o funcionamento interno do seu algoritmo possa ser adivinhado a partir das definições do membro variáveis (se assim for, provavelmente não é muito complicado e não vale a pena proteger em primeiro lugar).
 17
Author: the swine, 2014-09-23 11:18:03

Se a sua classe usar o idioma pimpl, pode evitar alterar o ficheiro de cabeçalho na classe pública.

Isto permite-lhe Adicionar/Remover métodos à classe pimpl, sem modificar o ficheiro de cabeçalho da classe externa. Você também pode adicionar / remover #inclui para o pimpl também.

Quando você muda o ficheiro de cabeçalho da classe externa, você tem que recompilar tudo o que #o Inclui (e se algum desses são ficheiros de cabeçalho, você tem que recompilar tudo o que #os Inclui, e assim on)

 14
Author: Nick, 2014-02-06 11:26:30

Tipicamente, a única referência à classe Pimpl no cabeçalho para a classe proprietária (Cat neste caso) seria uma declaração de avanço, como você fez aqui, porque isso pode reduzir grandemente as dependências.

Por exemplo, se a sua classe Pimpl tiver complicado a classificação como membro (e não apenas um ponteiro ou referência a ele), então terá de ter complicado a classificação completamente definida antes de ser usada. Na prática, isto significa incluir "Classe complicada".h " (que também indirectamente incluir qualquer classe complicada depende de). Isto pode levar a um único preenchimento de cabeçalho puxando em lotes e lotes de coisas, o que é ruim para gerenciar suas dependências (e seus tempos de compilação).

Quando você usa o pimpl idion, você só precisa #incluir o material usado na interface pública do seu tipo de proprietário (que seria Cat aqui). O que torna as coisas melhores para as pessoas que usam a sua biblioteca, e significa que você não precisa se preocupar com as pessoas dependendo de alguma parte interna da sua biblioteca - ou por engano, ou porque eles querem fazer algo que você não permite, então eles #definir público privado antes de incluir seus arquivos.

Se é uma classe simples, geralmente não há razão para usar um Pimpl, mas por vezes quando os tipos são bastante grandes, pode ser uma grande ajuda (especialmente em evitar longos tempos de construção) {[[2]}

 6
Author: Wilka, 2008-09-13 14:53:59
Bem, eu não o usaria. Tenho uma alternativa melhor:

Foo.h:

class Foo {
public:
    virtual ~Foo() { }
    virtual void someMethod() = 0;

    // This "replaces" the constructor
    static Foo *create();
}

Foo.cpp:

namespace {
    class FooImpl: virtual public Foo {

    public:
        void someMethod() { 
            //....
        }     
    };
}

Foo *Foo::create() {
    return new FooImpl;
}

Este padrão tem nome?

Como também programador Python e Java, gosto muito mais disto do que o idioma pImpl.

 4
Author: Esben Nielsen, 2011-06-21 13:30:54

Colocar a chamada para o impl->Purr dentro do ficheiro cpp significa que, no futuro, poderá fazer algo completamente diferente sem ter de alterar o ficheiro de cabeçalho. Talvez no próximo ano eles descubram um método auxiliar que poderiam ter chamado em vez disso e assim eles podem mudar o código para chamar isso diretamente e não usar impl->Purr em tudo. (Yes, they could achieve the same thing by updating the actual impl:: Purr method as well but in that case you are stuck with an extra function call that não alcança nada além de chamar a próxima função por sua vez)

Também significa que o cabeçalho só tem definições e não tem nenhuma implementação que faça com que uma separação mais limpa, que é todo o ponto do idioma.

 2
Author: Phil Wright, 2008-09-13 14:52:32
Acabei de implementar a minha primeira aula de pimpl nos últimos dias. Usei-o para eliminar os problemas que estava a ter, incluindo o winsock2.h em Borland Builder. Parecia estar estragando o alinhamento de estruturas e como eu tinha coisas de socket na classe dados privados, esses problemas estavam se espalhando para qualquer arquivo cpp que incluía o cabeçalho.

Usando pimpl, winsock2.h foi incluído em apenas um arquivo cpp onde eu poderia colocar uma tampa sobre o problema e não se preocupar que ele voltaria para morde-me.

Para responder a pergunta original, a vantagem que encontrei no encaminhamento de chamadas para o pimpl classe foi que o pimpl classe é a mesma que a sua classe original teria sido antes de pimpl isso, além de suas implementações não são distribuídos por 2 turmas em algumas estranhas da moda. É muito mais claro implementar o público para simplesmente avançar para a classe pimpl.

Como disse o Sr. Nodet, uma classe, uma responsabilidade.
 1
Author: markh44, 2009-03-24 16:34:13

Usamos o idioma PIMPL para emular programação orientada a aspectos onde os aspectos pré, post e erro são chamados antes e depois da execução de uma função membro.

struct Omg{
   void purr(){ cout<< "purr\n"; }
};

struct Lol{
  Omg* omg;
  /*...*/
  void purr(){ try{ pre(); omg-> purr(); post(); }catch(...){ error(); } }
};

Nós também usamos ponteiro para a classe base para compartilhar diferentes aspectos entre muitas classes.

A desvantagem desta abordagem é que o Utilizador da biblioteca tem de ter em conta todos os aspectos que vão ser executados, mas só vê a sua classe. É necessário navegar na documentação para qualquer secundario.

 1
Author: nurettin, 2014-11-11 18:20:59
Não sei se vale a pena mencionar esta diferença, mas ... ..

Seria possível ter a implementação no seu próprio espaço de nomes e ter um espaço de nomes público wrapper / library para o código que o utilizador vê:

catlib::Cat::Purr(){ cat_->Purr(); }
cat::Cat::Purr(){
   printf("purrrrrr");
}

Desta forma, todo o código da biblioteca pode fazer uso do espaço de nomes do gato e como a necessidade de expor uma classe para o usuário surge um invólucro pode ser criado no espaço de nomes do catlib.

 0
Author: JeffV, 2008-09-13 15:31:47
Acho que é revelador que, apesar de ser conhecido o idioma pimpl, não o vejo aparecer muitas vezes na vida real (por exemplo, em projetos de código aberto).

Muitas vezes pergunto-me se os" benefícios " são exagerados; Sim, você pode fazer alguns dos seus detalhes de implementação ainda mais escondidos, e sim, você pode mudar a sua implementação sem mudar o cabeçalho, mas não é óbvio que estas são grandes vantagens na realidade.

Isto é, não está claro que haja necessidade de sua implementação para ser que bem escondido, e talvez seja bastante raro que as pessoas realmente mudar apenas a implementação; assim que você precisa adicionar novos métodos, digamos, você precisa mudar o cabeçalho de qualquer maneira.
 0
Author: DrPizza, 2008-09-15 09:54:01