Por que colocamos funções de membros privados nos cabeçalhos?

14

A resposta para o porquê de colocar variáveis de membros privados em cabeçalhos C ++ é que o tamanho da classe deve ser conhecido nos pontos em que as instâncias são declaradas, para que o compilador possa gerar código que se move apropriadamente na pilha.

Por que precisamos colocar informações particulares membros nos cabeçalhos?

Mas há algum motivo para declarar funções privadas na definição de classe?

A alternativa seria essencialmente o idioma pimpl mas sem a indirecção supérflua.

Esse recurso de idioma é mais do que um erro histórico?

    
por Praxeolitic 15.05.2014 / 16:48
fonte

6 respostas

10

As funções de membros privados podem ser virtual e, em implementações comuns de C ++ (que usam vtable), a ordem específica e o número de funções virtuais precisam ser conhecidos por todos os clientes da classe. Isso se aplica mesmo se uma ou mais das funções de membro virtuais forem private .

Pode parecer que isso é como "colocar o carrinho antes do cavalo", porque as escolhas de implementação do compilador não devem afetar a especificação da linguagem. No entanto, na realidade, a própria linguagem C ++ foi desenvolvida ao mesmo tempo que uma implementação funcional ( Cfront ), que usava vtables.

    
por 15.05.2014 / 22:36
fonte
10

Se você permitisse que os métodos fossem adicionados a uma classe fora de sua definição, eles poderiam ser adicionados em qualquer lugar , em qualquer arquivo, por qualquer pessoa.

Isso forneceria acesso trivial a todo o código do cliente a membros de dados privados e protegidos.

Uma vez que você tenha terminado a definição de classe, não há como marcar alguns arquivos como sendo especialmente abençoados pelo autor para estendê-lo - existem apenas unidades de tradução simples. Assim, a única maneira razoável de dizer ao compilador que um determinado conjunto de métodos é oficial, ou abençoado pelo autor da classe, é declará-los dentro da classe.

Note que temos acesso direto à memória em C ++, o que significa que geralmente é trivial criar um tipo de sombra com o mesmo layout de memória da sua classe, adicionar meus próprios métodos (ou apenas tornar públicos todos os dados) e reinterpret_cast . Ou eu posso encontrar o código da sua função privada ou desmontá-lo. Ou pesquise o endereço da função na tabela de símbolos e ligue ou diretamente.

Esses especificadores de acesso não tentam impedir esses ataques, porque isso não é possível. Eles apenas indicam como uma classe deve ser usada.

    
por 15.05.2014 / 17:04
fonte
7

A resposta aceita explica isso para funções privadas virtuais , mas isso apenas responde a uma faceta específica da questão, que é consideravelmente mais limitada do que o que o OP pediu. Então, precisamos reformular: Por que precisamos declarar as funções privadas não virtuais nos cabeçalhos?

Outra resposta invoca o fato de que as classes devem ser declaradas em um bloco - após o qual elas são seladas e não podem ser adicionadas. Isso é o que você estaria fazendo, omitindo declarar um método privado no cabeçalho e tentando defini-lo em outro lugar. Bom ponto. Por que alguns usuários da classe podem aumentá-lo de uma maneira que outros usuários não podem observar? Métodos privados são parte disso e não são excluídos disso. Mas então você pergunta por que eles estão incluídos, e isso parece um pouco tautológico. Por que os usuários da turma devem saber sobre eles? Se eles não estivessem visíveis, os usuários não poderiam adicionar nenhum, e pronto.

Então, eu queria fornecer uma resposta que, em vez de incluir apenas métodos privados por padrão, fornece pontos específicos em favor de torná-los visíveis para os usuários. Uma razão mecanicista para funções privadas não virtuais que exigem declaração pública é dada em GotW # 100 de Herb Sutter sobre o idioma Pimpl como parte de sua lógica. Não vou falar sobre o Pimpl aqui, já que tenho certeza de que todos sabemos disso. Mas aqui está o bit relevante:

In C++, when anything in a header file class definition changes, all users of that class must be recompiled – even if the only change was to the private class members that the users of the class cannot even access. This is because C++’s build model is based on textual inclusion, and because C++ assumes that callers know two main things about a class that can be affected by private members:

  • Size and Layout: [of members and virtual functions - self-explanatory and great for performance, but not why we're here]
  • Functions: The calling code must be able to resolve calls to member functions of the class, including inaccessible private functions that overload with nonprivate functions — if the private function is a better match, the calling code will fail to compile. (C++ took the deliberate design decision to perform overload resolution before accessibility checking for safety reasons. For example, it was felt that changing the accessibility of a function from private to public shouldn’t change the meaning of legal calling code.)
Sutter é, naturalmente, uma fonte extremamente confiável como membro do Comitê, então ele sabe "uma deliberada decisão de design" quando ele vê um. E a ideia de exigir declaração pública de métodos privados como uma maneira de evitar semântica alterada ou acessibilidade quebrada acidentalmente é provavelmente a justificativa mais convincente. Felizmente, como a coisa toda parecia um tanto sem sentido antes!

    
por 10.07.2016 / 10:14
fonte
4

Existem duas razões para isso.

Primeiro, perceba que o especificador de acesso é para o compilador , e não é relevante no tempo de execução. Acessar um membro privado fora do escopo é um erro compilar .

Concisão

Considere uma função que é curta, uma ou duas linhas. Ele existe para reduzir a replicação de código em outro lugar, que também tem a vantagem de ser capaz de alterar a forma como um algoritmo ou qualquer outra coisa funciona em um lugar em vez de muitos (por exemplo, alterando um algoritmo de classificação).

Você prefere ter uma rápida ou duas linhas no cabeçalho ou ter o protótipo de função lá além de uma implementação em algum lugar? É mais fácil encontrar no cabeçalho e, para funções curtas, é muito mais detalhado ter uma implementação separada.

Existe outra grande vantagem, que é ...

Funções Inline

Uma função privada pode ser embutida, e isso necessariamente exige que ela esteja no cabeçalho. Considere isto:

class A {
  private:
    inline void myPrivateFunction() {
      ...
    }

  public:
    inline void somePublicFunction() {
      myPrivateFunction();
      ...
    }
};

A função privada pode ser capaz de ser alinhada junto com a função pública. Isso é feito a critério do compilador, pois a palavra-chave inline é tecnicamente uma sugestão , não um requisito.

    
por 15.05.2014 / 17:00
fonte
1

Outro motivo para ter métodos privados no arquivo de cabeçalho: há casos em que um método in-line público não faz muito mais do que chamar um ou vários métodos privados. Ter os métodos privados no cabeçalho significa que uma chamada para o método público pode ser completamente incorporada ao código real dos métodos privados, e o inline não é interrompido com uma chamada para o método privado. Mesmo a partir de uma unidade de compilação diferente (e métodos públicos seriam geralmente chamados de diferentes unidades de compilação).

É claro que há o motivo também de o compilador não conseguir detectar problemas com a resolução de sobrecarga se não conhecer todos os métodos, incluindo os privados.

    
por 10.07.2016 / 23:20
fonte
0

É permitir que essas funções acessem os membros privados. Caso contrário, você precisaria friend no cabeçalho.

Se qualquer função puder acessar os membros privados da classe, a privacidade será inútil.

    
por 15.05.2014 / 16:58
fonte

Tags