Pourquoi n’y a-t-il pas de classe de base en C ++?

Question rapide: du sharepoint vue de la conception, pourquoi, en C ++, il n’y a pas de classe de base pour tous, ce qui est généralement object dans d’autres langages?

La décision définitive se trouve dans les FAQ de Stroustrup . En bref, cela ne signifie aucun sens sémantique. Cela aura un coût. Les modèles sont plus utiles pour les conteneurs.

Pourquoi C ++ n’a-t-il pas une classe d’object universelle?

  • Nous n’en avons pas besoin: la programmation générique fournit des alternatives sûres de type statique dans la plupart des cas. D’autres cas sont traités en utilisant l’inheritance multiple.

  • Il n’y a pas de classe universelle utile: un vraiment universel ne porte pas de sémantique propre.

  • Une classe “universelle” encourage une reflection négligée sur les types et les interfaces et conduit à des vérifications excessives du temps d’exécution.

  • L’utilisation d’une classe de base universelle implique un coût: les objects doivent être alloués au tas pour être polymorphes; cela implique un coût de mémoire et d’access. Les objects tas ne supportent pas naturellement la sémantique de copie. Les objects tas ne prennent pas en charge les comportements simples (ce qui complique la gestion des ressources). Une classe de base universelle encourage l’utilisation de dynamic_cast et d’autres vérifications au moment de l’exécution.

Pensons d’abord à la raison pour laquelle vous voudriez avoir une classe de base en premier lieu. Je peux penser à quelques raisons différentes:

  1. Pour prendre en charge les opérations génériques ou les collections qui fonctionneront sur des objects de tout type.
  2. Inclure diverses procédures communes à tous les objects (telles que la gestion de la mémoire).
  3. Tout est un object (pas de primitives!). Certains langages (comme Objective-C) ne disposent pas de cette fonctionnalité, ce qui rend les choses assez compliquées.

Ce sont les deux bonnes raisons pour lesquelles les langages des marques Smalltalk, Ruby et Objective-C ont des classes de base (techniquement, Objective-C n’a pas vraiment de classe de base, mais à toutes fins utiles).

Pour # 1, la nécessité d’une classe de base qui unifie tous les objects sous une seule interface est évitée par l’inclusion de modèles en C ++. Par exemple:

 void somethingGeneric(Base); Derived object; somethingGeneric(object); 

est inutile lorsque vous pouvez maintenir l’intégrité du type à travers le polymorphism paramésortingque!

 template  void somethingGeneric(T); Derived object; somethingGeneric(object); 

Pour # 2, alors que dans Objective-C, les procédures de gestion de la mémoire font partie de l’implémentation d’une classe et sont héritées de la classe de base, la gestion de la mémoire en C ++ est effectuée en composition plutôt qu’en inheritance. Par exemple, vous pouvez définir un wrapper de pointeur intelligent qui effectuera le comptage de références sur des objects de tout type:

 template  struct refcounted { refcounted(T* object) : _object(object), _count(0) {} T* operator->() { return _object; } operator T*() { return _object; } void retain() { ++_count; } void release() { if (--_count == 0) { delete _object; } } private: T* _object; int _count; }; 

Ensuite, au lieu d’appeler des méthodes sur l’object lui-même, vous appelez des méthodes dans son wrapper. Cela permet non seulement une programmation plus générique: il vous permet également de séparer les préoccupations (puisque, idéalement, votre object devrait être plus concerné par ce qu’il devrait faire, que la manière dont sa mémoire devrait être gérée dans différentes situations).

Enfin, dans un langage comportant à la fois des primitives et des objects réels comme C ++, les avantages de disposer d’une classe de base (une interface cohérente pour toutes les valeurs) sont perdus, car certaines valeurs ne sont pas compatibles avec cette interface. Pour utiliser des primitives dans ce genre de situation, vous devez les placer dans des objects (si votre compilateur ne le fait pas automatiquement). Cela crée beaucoup de complications.

Donc, la réponse courte à votre question: C ++ n’a pas de classe de base car, ayant un polymorphism paramésortingque à travers des modèles, il n’est pas nécessaire.

Le paradigme dominant pour les variables C ++ est pass-by-value, pas pass-by-ref. Forcer tout à dériver d’un Object racine ferait passer par leur valeur une erreur ipse facto.

(Parce qu’accepter un object par valeur comme paramètre, par définition, le couperait et supprimerait son âme).

Ceci est importun C ++ vous fait réfléchir à la question de savoir si vous souhaitez une sémantique de valeur ou de référence, en vous donnant le choix. C’est une grande chose dans l’informatique de performance.

Le problème est qu’il existe un tel type en C ++! C’est void 🙂 Tout pointeur peut être implicitement converti à void * , y compris les pointeurs vers les types de base, les classes sans table virtuelle et les classes avec la table virtuelle.

Comme il doit être compatible avec toutes ces catégories d’objects, le void lui-même ne peut pas contenir de méthodes virtuelles. Sans fonctions virtuelles et RTTI, aucune information utile sur type ne peut être obtenue à partir de void (elle correspond à TOUS les types, donc elle ne peut indiquer que les choses vraies pour chaque type), mais les fonctions virtuelles et RTTI rendraient les types simples inefficaces être un langage adapté à la programmation de bas niveau avec access direct à la mémoire, etc.

Donc, il y a un tel type. Il fournit juste une interface très minimaliste (en fait, vide) en raison de la nature de bas niveau du langage. 🙂

C ++ est un langage fortement typé. Pourtant, il est curieux qu’il n’ait pas de type d’object universel dans le contexte de la spécialisation des modèles.

Prenez, par exemple, le motif

 template  class Hook; template  class Hook { ... ReturnType operator () (ArgTypes... args) { ... } }; 

qui peut être instancié comme

 Hook ...; 

Supposons maintenant que nous voulons la même chose pour une fonction particulière. Comme

 template  class Hook; template  class Hook { ... ReturnType operator () (ArgTypes... args) { ... } }; 

avec l’instanciation spécialisée

 Hook ... 

Mais hélas, même si la classe T peut remplacer n’importe quel type (classe ou non) avant la spécialisation, il n’y a pas auto fallback équivalent (j’utilise cette syntaxe comme le non-type générique le plus évident dans ce contexte) pour tout argument de modèle non typé avant spécialisation.

Donc, en général, ce modèle ne transfère pas des arguments de modèle de type à des arguments de type non-type.

Comme avec beaucoup de recoins dans le langage C ++, la réponse est probablement “aucun membre du comité n’y a pensé”.

C ++ s’appelait initialement “C avec les classes”. C’est une progression du langage C, contrairement à d’autres choses plus modernes comme C #. Et vous ne pouvez pas voir C ++ comme un langage, mais comme base de langues (Oui, je me souviens du livre de Scott Meyers, Effective C ++).

C lui-même est un mélange de langues, le langage de programmation C et son préprocesseur.

C ++ ajoute un autre mix:

  • l’approche classe / objects

  • des modèles

  • le STL

Personnellement, je n’aime pas certaines choses qui viennent directement de C en C ++. Un exemple est la fonctionnalité enum. La manière dont C # permet au développeur de l’utiliser est bien meilleure: il limite l’enum dans sa propre scope, il possède une propriété Count et il est facilement itérable.

Comme C ++ voulait être rétrocompatible avec C, le concepteur était très permissif pour permettre au langage C d’entrer dans C ++ (il y a quelques différences subtiles, mais je ne me souviens de rien que vous puissiez faire avec un compilateur C ne pouvait pas faire en utilisant un compilateur C ++).