Por que objetos da mesma classe têm acesso aos dados privados uns dos outros?

98

Por que objetos da mesma classe têm acesso aos dados privados uns dos outros?

class TrivialClass {
public: 
  TrivialClass(const std::string& data) :
    mData(data) {};

  const std::string& getData(const TrivialClass& rhs) const {
    return rhs.mData;
  };

private:
  std::string mData;
};

int main() {
  TrivialClass a("fish");
  TrivialClass b("heads");

  std::cout << "b via a = " << a.getData(b) << std::endl;
  return 0;
}

Este código funciona. É perfeitamente possível para o objeto a acessar dados privados do objeto be retorná-los. Por que deveria ser assim? Eu acho que os dados privados são privados. (Comecei tentando entender os construtores de cópia no idioma pimpl, mas depois descobri que nem mesmo entendia essa situação simples.)

Keith
fonte
18
Bem, como ponto de partida, você não seria capaz de implementar adequadamente nenhum construtor de cópia para nada além das classes mais simples. Você pode pensar nas aulas como sendo suas melhores amigas :-)
Cameron
4
Pense em particular de seus clientes, mas todos os funcionários da classe têm acesso
Martin Beckett
Obrigado Cameron. Isso faz sentido, mas por que esse acesso não está restrito apenas a construtores de cópia e operadores de atribuição?
Keith
5
Objetos do mesmo tipo freqüentemente interagem muito. E ninguém está forçando você a escrever um método que forneça dados privados de outra instância. :)
UncleBens
4
Simplesmente porque, em tempo de compilação, o compilador não tem como identificar seu mesmo objeto. Forçar esse acesso exigiria suporte em tempo de execução.
Chethan

Respostas:

80

Porque é assim que funciona em C ++. Em C ++, o controle de acesso funciona por classe , não por objeto.

O controle de acesso em C ++ é implementado como um recurso estático de tempo de compilação. Acho que é bastante óbvio que não é realmente possível implementar qualquer controle de acesso por objeto significativo em tempo de compilação. Apenas o controle por classe pode ser implementado dessa forma.

Algumas dicas de controle por objeto estão presentes na especificação de acesso protegido , razão pela qual ela ainda tem seu próprio capítulo dedicado no padrão (11.5). Mas ainda assim, quaisquer características por objeto descritas lá são bastante rudimentares. Novamente, o controle de acesso em C ++ deve funcionar por classe.

Formiga
fonte
9
+1. C ++ é grande em mecanismos de tempo de compilação, não tão grande em mecanismos de tempo de execução. Regra geral muito boa.
Nemo
4
Seu "não é realmente possível implementar qualquer controle de acesso por objeto significativo em tempo de compilação". Por que não? Em void X::f(X&x), o compilador é facilmente capaz de distinguir this->ae x.a. Nem (sempre) é possível para o compilador saber isso *thise se eles xsão realmente o mesmo objeto se x.f(x)for chamado, mas eu poderia muito bem ver um designer de linguagem achar isso OK.
André Caron
@ AndréCaron Eu acho que isso é na verdade uma chaleira de peixes muito maior do que você pensa que é. Quando o inlining não ocorre, o compilador sempre terá que fazer uma verificação se thise &xsão os mesmos. Para piorar, isso acaba sendo um problema mesmo com X::f(Y& y), porque nosso objeto concreto pode ser do tipo Zque herda de Xe Y. Resumindo, é uma verdadeira bagunça, sem desempenho, difícil de fazer com que o trabalho de IM seja sensato.
Nir Friedman
@NirFriedman Acho que você entendeu mal a sugestão. Ao compilar X::f(X& x), se houver acesso a x.a, ele não compilará. Nada mais muda, nenhuma verificação precisa ser inserida, então o desempenho de programas ainda válidos não é afetado. E não é sugerido como uma mudança significativa no C ++ existente, mas como algo que os designers poderiam ter feito na introdução privateoriginal.
Alexey Romanov
31

"Privado" não é realmente um mecanismo de controle de acesso no sentido de "Tornei minhas fotos privadas no Facebook para que você não possa vê-las".

Em C ++, "privado" simplesmente diz que essas são partes de uma classe que você (o codificador da classe) pode alterar em versões futuras, etc., e você não quer que outros programadores usando sua classe confiem em sua existência ou funcionalidade .

Se você deseja um controle de acesso verdadeiro, deve implementar técnicas genuínas de segurança de dados.

Vsekhar
fonte
13

Esta é uma boa pergunta e eu encontrei essa pergunta recentemente. Tive algumas discussões com meus colegas e aqui está o resumo de nossa discussão: Isso ocorre por design. Isso não significa que esse design seja totalmente razoável para todos os casos, mas deve haver algumas considerações sobre o porquê de cada classe privada ser escolhida. Os possíveis motivos que podemos pensar incluem:

Em primeiro lugar, o custo do controle de acesso por instância pode ser muito alto. Isso foi discutido por outros neste tópico. Em teoria, isso pode ser feito por meio desta verificação de ponteiro. No entanto, isso não pode ser feito em tempo de compilação e só pode ser feito em tempo de execução. Portanto, você deve identificar o controle de acesso de cada membro em tempo de execução e, quando for violado, possivelmente apenas exceções serão levantadas. O custo é alto.

Em segundo lugar, o controle de acesso por classe tem seu próprio caso de uso, como construtor de cópia ou operador =. Seria difícil implementá-los se o controle de acesso fosse por instância.

Além disso, o controle de acesso é principalmente da perspectiva de programação / linguagem, para saber como modularizar / controlar o acesso ao código / membro, não aos dados.

JackyZhu
fonte
12

É uma decisão arbitrária de design de linguagem. Em Ruby , por exemplo, privaterealmente significa privado, como em "apenas a instância pode acessar seus próprios membros de dados privados". No entanto, isso é um tanto restritivo.

Conforme apontado nos comentários, os construtores de cópia e os operadores de atribuição são locais comuns onde você acessa os membros de dados privados de outra instância diretamente. Existem razões menos óbvias para isso.

Considere o seguinte caso. Você está implementando uma lista vinculada OO. A lista vinculada possui uma classe de nó aninhada para gerenciar ponteiros. Você pode implementar essa classe de nó de forma que ela gerencie os ponteiros em si (em vez de ter os ponteiros públicos e gerenciados pela lista). Nesse caso, você teria os objetos de nó desejando modificar os ponteiros de outros objetos de nó em outros lugares que o construtor de cópia típico e o operador de atribuição.

André Caron
fonte
4

O truque é lembrar que os dados são privatepara a classe , não para a instância da classe. Qualquer método dentro de sua classe pode acessar os dados privados de qualquer instância dessa classe; não há uma maneira de manter os dados privados dentro de uma instância, a menos que você proíba os métodos que acessam explicitamente membros de dados privados de outras instâncias.

Adam Maras
fonte
1

Além de todas as respostas acima, considere construtores de cópia personalizados, operadores de atribuição e todas as outras funções que você escreveria para uma classe que operasse em outras instâncias . Você precisaria de funções de acessador para todos esses membros de dados.

Jacob
fonte
-8

Os dados privados permanecem privados até que alguém que tenha acesso a eles os revele a outra pessoa.

Este conceito também se aplica a outras situações, como:

class cMyClass
{
public:
   // ...
   // omitted for clarity
   // ...

   void Withdraw(int iAmount)
   {
      iTheSecretVault -= iAmount;
   }

private:
   int iTheSecretVault;
};

Como alguém poderia sacar o dinheiro? :)

YeenFei
fonte
3
Este exemplo não envolve uma instância de classe acessando membros de dados privados de outra instância.
André Caron
@Andre, "Este conceito se aplica a outras situações também, como ..."
YeenFei
^ "outra situação" está fora do tópico por definição, então seu exemplo não é relevante (e não tenho certeza de que seria informativo em qualquer outro lugar)
sublinhado_d
1
Esta não é uma explicação correta para a pergunta.
Panda de