Pergunta

Pelo que entendi, o idioma do PIMPL existe apenas porque o C ++ o força a colocar todos os membros da classe particular no cabeçalho. Se o cabeçalho conter apenas a interface pública, teoricamente, qualquer alteração na implementação da classe não exigiria um recompile para o restante do programa.

O que eu quero saber é por que o C ++ não foi projetado para permitir essa conveniência. Por que exige que as partes íntimas de uma classe sejam exibidas abertamente no cabeçalho (sem trocadilhos)?

Foi útil?

Solução

Isso tem a ver com o tamanho do objeto. O arquivo H é usado, entre outras coisas, para determinar o tamanho do objeto. Se os membros privados não forem dados nele, você não saberia o tamanho de um objeto para o novo.

Você pode simular, no entanto, o comportamento desejado pelo seguinte:

class MyClass
{
public:
   // public stuff

private:
#include "MyClassPrivate.h"
};

Isso não aplica o comportamento, mas retira as coisas privadas do arquivo .h. No lado negativo, isso adiciona outro arquivo para manter. Além disso, no Visual Studio, o IntelliSense não funciona para os membros particulares - isso pode ser uma mais ou menos.

Outras dicas

Eu acho que há uma confusão aqui. O problema não é sobre cabeçalhos. Os cabeçalhos não fazem nada (são apenas maneiras de incluir bits comuns de texto de origem entre vários arquivos de código de origem).

O problema, por mais que exista, é que as declarações de classe no C ++ precisam definir tudo, público e privado, que uma instância precisa ter para trabalhar. (O mesmo se aplica ao Java, mas a maneira como a referência às classes compiladas externamente trabalha torna o uso de qualquer coisa como cabeçalhos compartilhados desnecessários.)

É da natureza das tecnologias comuns orientadas a objetos (não apenas do C ++) que alguém precisa conhecer a classe concreta usada e como usar seu construtor para fornecer uma implementação, mesmo se você estiver usando apenas as partes públicas. O dispositivo em (3, abaixo) o esconde. A prática em (1, abaixo) separa as preocupações, quer você (3) ou não.

  1. Use classes abstratas que definem apenas as partes públicas, principalmente métodos, e deixe a classe de implementação herdar dessa classe abstrata. Portanto, usando a convenção usual para cabeçalhos, há um abstrato.HPP que é compartilhado. Há também uma implementação.HPP que declara a classe herdada e que só é transmitida para os módulos que implementam métodos da implementação. O arquivo implementation.hpp #include "abstract.hpp" para uso na declaração de classe que faz, para que haja um único ponto de manutenção para a declaração da interface abstraída.

  2. Agora, se você deseja aplicar o esconderijo da declaração da classe de implementação, precisa ter alguma maneira de solicitar a construção de uma instância concreta sem possuir a declaração de classe completa e específica: você não pode usar novo e não pode usar instâncias locais . (Você pode excluir.

  3. Juntamente com ou como parte do arquivo de cabeçalho que é usado como definição compartilhada para a classe/interface abstrata, inclua assinaturas de função para funções de auxiliar externo. Essas funções devem ser implementadas em módulos que fazem parte das implementações de classe específicas (para que vejam a declaração completa da classe e possam exercer o construtor). A assinatura da função auxiliar é provavelmente muito parecida com a do construtor, mas retorna uma referência de instância como resultado (esse proxy do construtor pode retornar um ponteiro nulo e pode até lançar exceções se você gosta desse tipo de coisa). A função ajudante constrói uma instância de implementação específica e retorna que ele foi referido a uma instância da classe abstrata.

Missão cumprida.

Ah, e a recompilação e o rolagem devem funcionar da maneira que você deseja, evitando a recompilação dos módulos de chamada quando apenas a implementação muda (pois o módulo de chamada não faz mais alocações de armazenamento para as implementações).

Vocês estão todos ignorando o ponto da pergunta -

Por que o desenvolvedor deve digitar o código PIMPL?

Para mim, a melhor resposta que posso encontrar é que não temos uma boa maneira de expressar código C ++ que permite operar nele. Por exemplo, o tempo de compilação (ou pré-processador, ou qualquer outra coisa) ou um código DOM.

O C ++ precisa muito de um ou de ambos disponíveis para um desenvolvedor para fazer metaprogramação.

Então você pode escrever algo assim em seu myclass público.h:

#pragma pimpl(MyClass_private.hpp)

E então escreva o seu próprio gerador de invólucro muito trivial.

Alguém terá uma resposta muito mais detalhada do que eu, mas a resposta rápida é dupla: o compilador precisa conhecer todos os membros de uma estrutura para determinar os requisitos de espaço de armazenamento, e o compilador precisa conhecer a ordem desses membros para gerar compensações de maneira determinística.

O idioma já é bastante complicado; Eu acho que um mecanismo para dividir as definições de dados estruturados no código seria um pouco de calamidade.

Normalmente, eu sempre vi classes de políticas usado para definir o comportamento de implementação em um manicolor do PIMPL. Eu acho que existem alguns benefícios adicionais do uso de um padrão de política - mais fácil de trocar implementações, pode combinar facilmente várias implementações parciais em uma única unidade que permite dividir o código de implementação em unidades funcionais e reutilizáveis, etc.

Pode ser porque o tamanho da classe é necessário ao passar em sua instância por valores, agregá -la em outras classes, etc?

Se o C ++ não suportasse a semântica do valor, teria sido bom, mas acontece.

Sim mas...

Você precisa ler o livro "Design e evolução de C ++" da Stroustrup. Teria inibido a captação de C ++.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top