Pourquoi les variables privées sont-elles décrites dans le fichier d'en-tête accessible au public?

11

OK, donc j'espère que c'est une question assez subjective pour les programmeurs, mais voilà. J'élargis continuellement ma connaissance des langages et des pratiques de génie logiciel ... et j'ai rencontré quelque chose qui n'a absolument aucun sens pour moi.

En C ++, les déclarations de classe incluent des private:méthodes et des paramètres dans le fichier d'en-tête, qui, théoriquement, est ce que vous transmettez à l'utilisateur si vous en faites une lib.

Dans Objective-C, les @interfaces font à peu près la même chose, vous forçant à répertorier vos membres privés (au moins, il existe un moyen d'obtenir des méthodes privées dans le fichier d'implémentation).

D'après ce que je peux dire, Java et C # vous permettent de fournir une interface / un protocole qui peut déclarer toutes les propriétés / méthodes accessibles au public et donne au codeur la possibilité de masquer tous les détails de l'implémentation dans le fichier d'implémentation.

Pourquoi? L'encapsulation est l'un des principaux principes de la POO, pourquoi le C ++ et l'Obj-C n'ont-ils pas cette capacité de base? Existe-t-il une sorte de solution de contournement des meilleures pratiques pour Obj-C ou C ++ qui masque toute l' implémentation?

Merci,

Stephen Furlani
la source
4
Je ressens ta douleur. Je me suis enfui en hurlant de C ++ la première fois que j'ai ajouté un champ privé à une classe et j'ai dû recompiler tout ce qui l'utilisait.
Larry Coleman
@ Larry, cela ne me dérange pas trop, mais il semble être annoncé comme un excellent langage OO, mais il ne peut même pas encapsuler "correctement".
Stephen Furlani
2
Ne croyez pas le battage médiatique. Il existe de meilleures façons de faire OO, à la fois dans les camps de frappe statique et dynamique.
Larry Coleman
C'est un grand langage à certains égards, mais pas à cause de ses capacités OO, qui sont une greffe de Simula 67 sur C.
David Thornley
Vous devriez faire de la programmation C. Ensuite, vous comprendrez mieux pourquoi ces langages sont tels qu'ils sont, y compris Java C # etc.
Henry

Réponses:

6

La question est de savoir si le compilateur doit savoir la taille d'un objet. Si c'est le cas, alors le compilateur doit connaître les membres privés afin de les compter.

En Java, il existe des types et des objets primitifs, et tous les objets sont alloués séparément, et les variables qui les contiennent sont vraiment des pointeurs. Par conséquent, puisqu'un pointeur est un objet de taille fixe, le compilateur sait quelle taille représente une variable, sans connaître la taille réelle de l'objet pointé. Le constructeur gère tout cela.

En C ++, il est possible d'avoir des objets représentés localement ou sur le tas. Par conséquent, le compilateur doit connaître la taille d'un objet pour pouvoir allouer une variable ou un tableau local.

Parfois, il est souhaitable de diviser la fonctionnalité de classe en une interface publique et un tout privé, et c'est là que la technique PIMPL (Pointer to IMPLementation) entre en jeu. La classe aura un pointeur vers une classe d'implémentation privée, et les méthodes de classe publique peuvent appeler cette.

David Thornley
la source
le compilateur ne peut-il pas consulter la bibliothèque d'objets pour obtenir ces informations? Pourquoi ces informations ne peuvent-elles pas être lues par machine au lieu d'être lisibles par l'homme?
Stephen Furlani
@Stephen: Au moment de la compilation, il n'y a pas nécessairement de bibliothèque d'objets. Étant donné que la taille des objets affecte le code compilé, il doit être connu au moment de la compilation, pas seulement au moment du lien. De plus, il est possible pour Ah de définir la classe A, Bh pour définir la classe B et les définitions de fonction dans A.cpp pour utiliser des objets de classe B et vice versa. Dans ce cas, il serait nécessaire de compiler chacun de A.cpp et B.cpp avant l'autre, si vous prenez la taille de l'objet à partir du code objet.
David Thornley
la liaison ne peut-elle pas être effectuée pendant la compilation? J'avoue ne pas connaître les détails à cette profondeur, mais si l'interface d'un objet expose son contenu, ces mêmes contenus ne peuvent-ils pas être exposés à partir d'une bibliothèque ou d'un autre composant lisible par machine? doivent- ils être définis dans un fichier d'en-tête accessible au public? Je comprends que cela fait partie de la majorité des langages C, mais est - ce que l'être?
Stephen Furlani
1
@Stephen: La liaison ne peut être effectuée pendant la compilation que si tous les composants appropriés sont présents. Même alors, ce serait un processus compliqué, impliquant une compilation partielle (tout sauf les tailles des objets), une liaison partielle (pour obtenir les tailles des objets), puis une compilation finale et une liaison. Étant donné que C et C ++ sont des langages relativement anciens, cela n'allait tout simplement pas se produire. Vraisemblablement, quelqu'un pourrait concevoir un nouveau langage sans fichiers d'en-tête, mais ce ne serait pas C ++.
David Thornley
14

En raison de la conception de C ++, afin de créer un objet sur la pile, le compilateur doit savoir sa taille. Pour ce faire, il doit avoir tous les champs présents dans le fichier d'en-tête, car c'est tout ce que le compilateur peut voir lorsque l'en-tête est inclus.

Par exemple, si vous définissez une classe

class Foo {
    public int a;
    private int b;
};

sizeof(Foo)est alors sizeof(a) + sizeof(b). S'il y avait un mécanisme pour séparer les champs privés, l'en-tête pourrait contenir

class Foo {
    public int a;
};

avec sizeof(Foo) = sizeof(a) + ???.

Si vous voulez vraiment cacher des données privées, essayez l'idiome pimpl, avec

class FooImpl;
class Foo {
    private FooImpl* impl;
}

dans l'en-tête et une définition pour FooImpluniquement dans Foole fichier d'implémentation de.

Scott Wales
la source
Oh. Je ne savais pas que c'était le cas. Est-ce la raison pour laquelle des langages tels que Java, C # et Obj-C ont des "objets de classe" afin qu'ils puissent demander à la classe quelle taille doit avoir l'objet?
Stephen Furlani
4
Essayez d'utiliser un bouton, un pointeur vers une implémentation privée. Étant donné que tous les pointeurs de classe ont la même taille, vous n'avez pas à définir la classe d'implémentation, déclarez-la uniquement.
Scott Wales
Je pense que cela devrait être une réponse plutôt qu'un commentaire.
Larry Coleman
1

Tout cela se résume au choix de conception.

Si vous souhaitez vraiment masquer les détails d'implémentation privée de la classe C ++ ou Objective-C, vous fournissez une ou plusieurs interfaces prises en charge par la classe (classe virtuelle pure C ++, Objective-C @protocol) et / ou vous créez la classe capable de se construire en fournissant une méthode d'usine statique ou un objet d'usine de classe.

La raison pour laquelle les variables privées sont exposées dans le fichier d'en-tête / déclaration de classe / interface @ est qu'un consommateur de votre classe peut avoir besoin d'en créer une nouvelle instance et que a new MyClass()ou [[MyClass alloc]init]dans le code client a besoin du compilateur pour comprendre la taille d'une MyClass l'objet est de faire l'allocation.

Java et C # ont également leurs variables privées détaillées dans la classe - ils ne font pas exception à cela, mais IMO le paradigme d'interface est beaucoup plus courant avec ces langages. Vous pouvez ne pas avoir le code source dans chaque cas, mais il y a suffisamment de métadonnées dans le code compilé / octet pour déduire ces informations. Comme C ++ et Objective-C n'ont pas ces métadonnées, la seule option est les détails réels de l'interface class / @. Dans le monde C ++ COM, vous n'exposez pas les variables privées d'aucune classe mais vous pouvez fournir le fichier d'en-tête - car la classe est pure virtuelle. Un objet de fabrique de classe est également enregistré pour créer les instances réelles, et il existe des métadonnées sous diverses formes.

En C ++ et Objective-C, il est moins difficile de distribuer le fichier d'en-tête que d'écrire et de maintenir un fichier de protocole interface / @ supplémentaire. C'est une des raisons pour lesquelles vous voyez si souvent l'implémentation privée exposée.

Une autre raison en C ++ est les modèles - le compilateur a besoin de connaître les détails de la classe afin de générer une version de cette classe spécialisée aux paramètres fournis. La taille des membres de la classe varie en fonction du paramétrage, il doit donc disposer de ces informations.

JBRWilkinson
la source