web-dev-qa-db-fra.com

Pourquoi mettons-nous des fonctions de membre privé dans les en-têtes?

La réponse à la raison pour laquelle nous plaçons des variables de membre privé dans les en-têtes C++ est que la taille de la classe doit être connue aux points où les instances sont déclarées afin que le compilateur puisse générer du code qui se déplace correctement dans la pile.

Pourquoi devons-nous mettre des membres privés dans les en-têtes?

Mais y a-t-il une raison de déclarer des fonctions privées dans la définition de classe?

L'alternative serait essentiellement l'idiome pimpl mais sans l'indirection superflue.

Cette fonctionnalité linguistique est-elle plus qu'une erreur historique?

16
Praxeolitic

Les fonctions membres privées peuvent être virtual, et dans les implémentations courantes de C++ (qui utilisent une table virtuelle), l'ordre et le nombre spécifiques de fonctions virtuelles doivent être connus de tous les clients de la classe. Cela s'applique même si une ou plusieurs des fonctions membres virtuelles sont private.

Il peut sembler que cela revient à "mettre le chariot avant le cheval", car les choix d'implémentation du compilateur ne devraient pas affecter la spécification du langage. Cependant, en réalité, le langage C++ lui-même a été développé en même temps qu'une implémentation fonctionnelle ( Cfront ), qui utilisait vtables.

11
Greg Hewgill

Si vous autorisez l'ajout de méthodes à une classe en dehors de sa définition, elles peuvent être ajoutées n'importe où, dans n'importe quel fichier, par n'importe qui.

Cela donnerait immédiatement à tous les codes clients un accès trivial aux membres de données privés et protégés.

Une fois que vous avez terminé la définition de la classe, il n'y a aucun moyen de marquer certains fichiers comme spécialement bénis par l'auteur pour l'étendre - il n'y a que des unités de traduction plates. Ainsi, la seule façon raisonnable de dire au compilateur qu'un ensemble particulier de méthodes est officiel, ou béni par l'auteur de la classe, est de les déclarer à l'intérieur de la classe.


Notez que nous avons un accès direct à la mémoire en C++, ce qui signifie qu'il est généralement trivial de créer un type d'ombre avec la même disposition de mémoire que votre classe, d'ajouter mes propres méthodes (ou simplement de rendre toutes les données publiques), et reinterpret_cast. Ou je peux trouver le code de votre fonction privée, ou le démonter. Ou recherchez l'adresse de la fonction dans la table des symboles et appelez ou directement.

Ces spécificateurs d'accès n'essaient pas d'empêcher ces attaques, car cela n'est pas possible. Ils indiquent uniquement comment une classe est censée être utilisée.

14
Useless

La réponse acceptée explique cela pour virtuel fonctions privées, mais cela ne répond qu'à une facette spécifique de la question, qui est considérablement plus limitée que ce que l'OP a demandé. Donc, nous devons reformuler: Pourquoi sommes-nous tenus de déclarer non virtuel des fonctions privées dans les en-têtes?

Une autre réponse invoque le fait que les classes doivent être déclarées dans un bloc - après quoi elles sont scellées et ne peuvent pas être ajoutées. C'est ce que vous feriez en omettant de déclarer une méthode privée dans l'en-tête puis en essayant de la définir ailleurs. Joli point. Pourquoi certains utilisateurs de la classe devraient-ils pouvoir l'augmenter d'une manière que d'autres utilisateurs ne peuvent pas observer? Les méthodes privées en font partie et n'en sont pas exclues. Mais alors vous demandez pourquoi ils sont inclus, et cela semble un peu tautologique. Pourquoi les utilisateurs de classe doivent-ils les connaître? S'ils n'étaient pas visibles, les utilisateurs ne pouvaient pas en ajouter, et hé hop.

Donc, je voulais apporter une réponse qui, plutôt que de simplement inclure des méthodes privées par défaut, fournit des points spécifiques en faveur de leur visibilité pour les utilisateurs. Une raison mécanique pour les fonctions privées non virtuelles nécessitant une déclaration publique est donnée dans Herb Sutter's GotW # 100 sur l'idiome Pimpl dans le cadre de sa justification. Je ne parlerai pas de Pimpl ici, car je suis sûr que nous le savons tous. Mais voici le morceau pertinent:

En C++, lorsque quoi que ce soit dans une définition de classe de fichier d'en-tête change, tous les utilisateurs de cette classe doivent être recompilés - même si la seule modification concerne les membres de classe privés auxquels les utilisateurs de la classe ne peuvent même pas accéder. En effet, le modèle de génération de C++ est basé sur l'inclusion textuelle et parce que C++ suppose que les appelants connaissent deux choses principales à propos d'une classe qui peut être affectée par des membres privés:

  • Taille et disposition : [des membres et des fonctions virtuelles - explicites et parfaits pour la performance, mais pas pourquoi nous sommes ici]
  • Fonctions : Le code appelant doit être capable de résoudre les appels aux fonctions membres de la classe, y compris les fonctions privées inaccessibles qui surcharge avec des fonctions non privées - si la fonction privée correspond mieux, le code appelant ne pourra pas être compilé. (C++ a pris la décision délibérée de concevoir une résolution de surcharge avant de vérifier l'accessibilité pour des raisons de sécurité. Par exemple, il a été estimé que le fait de changer l'accessibilité d'une fonction du privé au public ne devrait pas changer la signification du code d'appel légal.)

Sutter est, bien sûr, une source extrêmement fiable en tant que membre du Comité, donc il connaît "une décision délibérée de conception" quand il en voit une. Et l'idée d'exiger la déclaration publique de méthodes privées comme moyen d'éviter une sémantique altérée ou une accessibilité accidentellement rompue plus tard est probablement la justification la plus convaincante. Heureusement, car le tout semblait plutôt inutile avant maintenant!

9
underscore_d

Il y a deux raisons à cela.

Tout d'abord, sachez que le spécificateur d'accès est destiné au compilateur et n'est pas pertinent au moment de l'exécution. L'accès à un membre privé en dehors de la portée est une erreur de compilation .

Concision

Prenons une fonction courte, une ou deux lignes. Il existe pour réduire la réplication de code ailleurs, ce qui a également l'avantage de pouvoir changer la façon dont un algorithme ou tout ce qui fonctionne en un seul endroit au lieu de plusieurs (par exemple, changer un algorithme de tri).

Souhaitez-vous plutôt avoir une ou deux lignes rapides dans l'en-tête, ou avoir le prototype de fonction plus une implémentation quelque part? Il est plus facile à trouver dans l'en-tête, et pour les fonctions courtes, il est beaucoup plus détaillé d'avoir une implémentation distincte.

Il y a un autre avantage majeur, qui est ...

Fonctions en ligne

Une fonction privée peut être en ligne, et cela nécessite nécessairement qu'elle soit dans l'en-tête. Considère ceci:

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

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

La fonction privée peut être alignée avec la fonction publique. Cela se fait à la discrétion du compilateur, car le mot clé inline est techniquement un suggestion , pas une exigence.

4
user22815

Une autre raison d'avoir des méthodes privées dans le fichier d'en-tête: Il existe des cas où une méthode en ligne publique ne fait pas beaucoup plus que d'appeler une ou plusieurs méthodes privées. Le fait d'avoir les méthodes privées dans l'en-tête signifie qu'un appel à la méthode publique peut être complètement aligné sur le code réel des méthodes privées, et l'inline ne s'arrête pas avec un appel à la méthode privée. Même à partir d'une unité de compilation différente (et les méthodes publiques seraient généralement appelées à partir de différentes unités de compilation).

Bien sûr, il y a aussi la raison pour laquelle le compilateur ne peut pas détecter les problèmes de résolution de surcharge s'il ne connaît pas toutes les méthodes, y compris les méthodes privées.

2
gnasher729

C'est pour permettre à ces fonctions d'accéder aux membres privés. Sinon, vous devrez les friend dans l'en-tête de toute façon.

Si une fonction pouvait accéder aux membres privés de la classe, alors private serait inutile.

0
ratchet freak