Pourquoi les objets de la même classe ont-ils accès aux données privées de chacun?

99

Pourquoi les objets de la même classe ont-ils accès aux données privées de chacun?

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;
}

Ce code fonctionne. Il est parfaitement possible pour l'objet a d'accéder aux données privées de l'objet b et de les renvoyer. Pourquoi devrait-il en être ainsi? Je pense que les données privées sont privées. (J'ai commencé par essayer de comprendre les constructeurs de copie dans l'idiome pimpl, mais j'ai ensuite découvert que je ne comprenais même pas cette situation simple.)

Keith
la source
18
Eh bien, pour commencer, vous ne seriez pas en mesure d'implémenter correctement des constructeurs de copie pour autre chose que les classes les plus simples. Vous pouvez penser aux cours comme étant leur propre meilleur ami :-)
Cameron
4
Pensez au privé de leurs clients, mais tous les employés de la classe ont accès
Martin Beckett
Merci Cameron. Cela a du sens, mais pourquoi cet accès n'est-il pas limité aux seuls constructeurs de copie et opérateurs d'affectation?
Keith
5
Les objets du même type interagissent souvent beaucoup. Et personne ne vous oblige à écrire une méthode qui distribue les données privées d'une autre instance. :)
UncleBens
4
Tout simplement parce qu'au moment de la compilation, le compilateur n'a aucun moyen d'identifier son même objet. L'application d'un tel accès nécessiterait une prise en charge au moment de l'exécution.
Chethan

Réponses:

80

Parce que c'est ainsi que cela fonctionne en C ++. En C ++, le contrôle d'accès fonctionne sur une base par classe , pas sur une base par objet.

Le contrôle d'accès en C ++ est implémenté en tant que fonctionnalité statique au moment de la compilation. Je pense qu'il est assez évident qu'il n'est pas vraiment possible d'implémenter un contrôle d'accès par objet significatif au moment de la compilation. Seul le contrôle par classe peut être implémenté de cette façon.

Certains indices de contrôle par objet sont présents dans la spécification d' accès protégé , c'est pourquoi elle a même son propre chapitre dédié dans la norme (11.5). Mais toutes les fonctionnalités par objet décrites ici sont plutôt rudimentaires. Encore une fois, le contrôle d'accès en C ++ est censé fonctionner par classe.

Fourmi
la source
9
+1. C ++ est gros sur les mécanismes de compilation, pas si gros sur les mécanismes d'exécution. Assez bonne règle générale.
Nemo
4
Votre "il n'est pas vraiment possible d'implémenter un contrôle d'accès par objet significatif au moment de la compilation". Pourquoi pas? Dans void X::f(X&x), le compilateur est facilement capable de distinguer this->aet x.a. Il n'est pas (toujours) possible pour le compilateur de savoir cela *thiset xsont en fait le même objet si x.f(x)est invoqué, mais je pourrais très bien voir un concepteur de langage trouver cela OK.
André Caron
@ AndréCaron Je pense que c'est en fait une marmite de poisson beaucoup plus grande que vous le faites. Lorsque l'inlining ne se produit pas, le compilateur devra toujours vérifier si thiset &xsont identiques. Pour aggraver les choses, cela finit par être un problème même avec X::f(Y& y), car notre objet concret pourrait être d'un type Zqui hérite des deux Xet Y. Bref, c'est un vrai bordel, pas performant, difficile à faire travailler raisonnablement avec MI.
Nir Friedman
@NirFriedman Je pense que vous avez mal compris la suggestion. Lors de la compilation X::f(X& x), s'il y a des accès à x.a, il ne compilerait pas. Rien d'autre ne change, aucune vérification ne doit être insérée, les performances des programmes encore valides ne sont donc pas affectées. Et cela n'est pas suggéré comme un changement radical du C ++ existant, mais comme quelque chose que les concepteurs auraient pu faire lors de l'introduction privateinitiale.
Alexey Romanov
31

«Privé» n'est pas vraiment un mécanisme de contrôle d'accès dans le sens de «J'ai rendu mes photos privées sur Facebook pour que vous ne puissiez pas les voir».

En C ++, «privé» dit simplement qu'il s'agit de parties d'une classe que vous (le codeur de la classe) pourriez changer dans les versions futures, etc., et vous ne voulez pas que les autres codeurs utilisant votre classe s'appuient sur leur existence ou leurs fonctionnalités .

Si vous souhaitez un véritable contrôle d'accès, vous devez mettre en œuvre de véritables techniques de sécurité des données.

vsekhar
la source
13

C'est une bonne question et je l'ai rencontrée récemment. J'ai eu quelques discussions avec mes collègues et voici le résumé de notre discussion: C'est voulu. Cela ne signifie pas que cette conception est totalement raisonnable pour tous les cas, mais il doit y avoir des considérations pour lesquelles chaque classe privée est choisie. Les raisons possibles auxquelles nous pourrions penser incluent:

Tout d'abord, le coût du contrôle d'accès par instance peut être très élevé. Cela a été discuté par d'autres dans ce fil. En théorie, cela peut être fait via cette vérification du pointeur. Cependant, cela ne peut pas être fait au moment de la compilation et ne peut être fait qu'au moment de l'exécution. Vous devez donc identifier le contrôle d'accès de chaque membre au moment de l'exécution, et en cas de violation, seules des exceptions seront déclenchées. Le coût est élevé.

Deuxièmement, le contrôle d'accès par classe a son propre cas d'utilisation, comme le constructeur de copie ou l'opérateur =. Il serait difficile de les implémenter si le contrôle d'accès est par instance.

De plus, le contrôle d'accès est principalement du point de vue de la programmation / du langage, pour savoir comment modulariser / contrôler l'accès au code / membre, pas aux données.

JackyZhu
la source
12

C'est en quelque sorte une décision arbitraire de conception de langage. Dans Ruby , par exemple, privatesignifie vraiment privé, comme dans "seule l'instance peut accéder à ses propres données privées membres". Cependant, c'est quelque peu restrictif.

Comme indiqué dans les commentaires, les constructeurs de copie et les opérateurs d'affectation sont des endroits courants où vous accédez directement aux membres de données privées d'une autre instance. Il y a des raisons moins évidentes.

Prenons le cas suivant. Vous implémentez une liste chaînée OO. La liste chaînée a une classe de nœuds imbriqués pour la gestion des pointeurs. Vous pouvez implémenter cette classe de nœuds de telle sorte qu'elle gère les pointeurs elle-même (plutôt que d'avoir les pointeurs publics et gérés par la liste). Dans un tel cas, les objets nœud voudraient modifier les pointeurs d'autres objets nœud à d'autres endroits que le constructeur et l'opérateur d'affectation typiques copient.

André Caron
la source
4

L'astuce consiste à se rappeler que les données appartiennent privateà la classe et non à l' instance de la classe. Toute méthode de votre classe peut accéder aux données privées de toute instance de cette classe; il n'y a pas de moyen de garder les données privées dans une instance, sauf si vous interdisez les méthodes qui accèdent explicitement aux données privées membres d'autres instances.

Adam Maras
la source
1

En plus de toutes les réponses ci-dessus, considérez les constructeurs de copie personnalisés, les opérateurs d'affectation et toutes les autres fonctions que vous écririez pour une classe qui opèrent sur d' autres instances . Vous auriez besoin de fonctions d'accesseur pour tous ces membres de données.

Jacob
la source
-8

Les données privées restent privées jusqu'à ce que quelqu'un qui y ait accès les révèle à d'autres.

Ce concept s'applique également à d'autres situations, telles que:

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

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

private:
   int iTheSecretVault;
};

Comment quelqu'un pourrait-il retirer l'argent? :)

YeenFei
la source
3
Cet exemple n'implique pas qu'une instance de classe accède aux membres de données privés d'une autre instance.
André Caron
@Andre, "Ce concept s'applique également à d'autres situations, telles que ..."
YeenFei
^ "autre situation" est hors sujet par définition, donc votre exemple n'a aucune pertinence (et je ne suis pas sûr qu'il soit informatif ailleurs non plus)
underscore_d
1
Ce n'est pas du tout une explication correcte à la question.
Panda