Pourquoi les objets de la même classe ont-ils accès aux données privées les uns des autres?
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 en serait-il 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 découvert que je ne comprenais même pas cette situation simple.)
Parce que c'est comme ça que ça fonctionne en C++. En C++, le contrôle d'accès fonctionne sur la base par classe, pas sur la 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 significatif par objet au moment de la compilation. Seul le contrôle par classe peut être implémenté de cette façon.
Quelques indices de contrôle par objet sont présents dans la spécification accès protégé, c'est pourquoi il a même son propre chapitre dédié dans la norme (11.5). Mais toutes les caractéristiques par objet décrites y sont encore assez rudimentaires. Encore une fois, le contrôle d'accès en C++ est censé fonctionner par classe.
"Privé" n'est pas vraiment un mécanisme contrôle d'accès dans le sens de "J'ai rendu mes photos privées sur Facebook afin que vous ne puissiez pas les voir."
En C++, "privé" dit simplement que ce sont des parties d'une classe que vous (le codeur de la classe) pourriez changer dans les futures versions, etc., et vous ne voulez pas que d'autres codeurs utilisant votre classe s'appuient sur leur existence ou leur fonctionnalité .
Si vous souhaitez un véritable contrôle d'accès, vous devez implémenter de véritables techniques de sécurité des données.
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 par conception. Cela ne signifie pas que cette conception est totalement raisonnable dans tous les cas, mais il doit y avoir des considérations pour lesquelles par classe privé est choisi. Les raisons possibles auxquelles nous pourrions penser incluent:
Tout d'abord, le coût du contrôle d'accès par instance pourrait être très élevé. Cela a été discuté par d'autres dans ce fil. En théorie, cela peut être fait via this check 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 lorsqu'il est violé, seules des exceptions seront levé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 mettre en œuvre si le contrôle d'accès se fait par instance.
De plus, le contrôle d'accès est principalement du point de vue programmation/langage, pour savoir comment modulariser/contrôler l'accès au code/membre, pas aux données.
Il s'agit en quelque sorte d'une décision arbitraire de conception de langage. Dans Ruby , par exemple, private
signifie vraiment privé, car dans "seule l'instance peut accéder à ses propres membres de données privées". Cependant, cela est quelque peu restrictif.
Comme indiqué dans les commentaires, les constructeurs de copie et les opérateurs d'affectation sont des endroits communs où vous accédez directement aux membres de données privées d'une autre instance. Il y a des raisons moins évidentes.
Considérez le cas suivant. Vous implémentez une liste de liens OO. La liste de liens a une classe de nœuds imbriquée 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 le pointeurs publics et gérés par la liste). Dans un tel cas, vous auriez les objets de noeud voulant modifier les pointeurs d'autres objets de noeud à d'autres endroits que le constructeur de copie typique et l'opérateur d'affectation.
L'astuce consiste à se rappeler que les données sont private
à la classe, pas à la instance de la classe. Toute méthode de votre classe peut accéder aux données privées de n'importe quelle instance de cette classe; il n'y a aucun 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 des membres d'autres instances.
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 fonctionne sur autres instances. Vous auriez besoin de fonctions d'accesseur pour tous ces membres de données.