Question rapide: du point de vue de la conception, pourquoi est-ce que, en C ++, il n'y a pas de classe de base mère de tous, qu'est-ce qui est généralement object
dans d'autres langages?
c++
language-design
slezica
la source
la source
typedef
s ponctuels . Avec une hiérarchie de classes plus générique, vous pouvez simplement utiliserobject
ouiterator
.Réponses:
La décision définitive se trouve dans la FAQ de Stroustrup . Bref, cela ne transmet aucune signification sémantique. Cela aura un coût. Les modèles sont plus utiles pour les conteneurs.
la source
Objects must be heap-allocated to be polymorphic
- Je ne pense pas que cette déclaration soit généralement correcte. Vous pouvez certainement créer une instance d'une classe polymorphe sur la pile et la transmettre en tant que pointeur vers l'une de ses classes de base, ce qui donne un comportement polymorphe.Foo
convertir une référence-à-en-référence-à-Bar
quandBar
n'hérite pasFoo
, lève de manière déterministe une exception. En C ++, tenter de convertir un pointeur vers Foo en un pointeur vers barre pourrait renvoyer un robot dans le temps pour tuer Sarah Connor.Voyons d'abord pourquoi vous voudriez avoir une classe de base en premier lieu. Je peux penser à plusieurs raisons différentes:
Ce sont les deux bonnes raisons pour lesquelles les langages de la marque 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, il en a).
Pour # 1, le besoin d'une classe de base qui unifie tous les objets sous une seule interface est évité par l'inclusion de modèles dans C ++. Par exemple:
void somethingGeneric(Base); Derived object; somethingGeneric(object);
n'est pas nécessaire, lorsque vous pouvez maintenir l'intégrité du type tout au long du polymorphisme paramétrique!
template <class T> void somethingGeneric(T); Derived object; somethingGeneric(object);
Pour le n ° 2, alors qu'en 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 utilisant la composition plutôt que l'héritage. Par exemple, vous pouvez définir un wrapper de pointeur intelligent qui effectuera le comptage de références sur des objets de tout type:
template <class T> 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'objet lui-même, vous appelleriez des méthodes dans son wrapper. Cela permet non seulement une programmation plus générique: cela vous permet également de séparer les préoccupations (car idéalement, votre objet devrait être plus préoccupé par ce qu'il doit faire, que par la façon dont sa mémoire doit être gérée dans différentes situations).
Enfin, dans un langage qui a à la fois des primitives et des objets réels comme C ++, les avantages d'avoir une classe de base (une interface cohérente pour chaque valeur) sont perdus, car vous avez alors certaines valeurs qui ne peuvent pas se conformer à cette interface. Afin d'utiliser des primitives dans ce genre de situation, vous devez les transformer en objets (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 polymorphisme paramétrique via des modèles, il n'en a pas besoin.
la source
object
(System.Object
), mais elles n'ont pas besoin de l'être. Pour le compilateur,int
etSystem.Int32
sont des alias et peuvent être utilisés de manière interchangeable; le runtime gère la boxe en cas de besoin.std::shared_ptr
ce qui devrait être utilisé à la place.Le paradigme dominant pour les variables C ++ est le pass-by-value et non le pass-by-ref. Forcer tout à dériver d'une racine
Object
ferait de leur passage par valeur une erreur ipse facto.(Parce qu'accepter un objet par valeur comme paramètre, le couperait par définition et enlèverait son âme).
Ceci n'est pas le bienvenu. C ++ vous fait réfléchir à savoir si vous vouliez une sémantique de valeur ou de référence, vous donnant le choix. C'est une chose importante dans le calcul des performances.
la source
Object
serait relativement faible.Le problème est qu'il existe un tel type en C ++! Ça l'est
void
. :-) Tout pointeur peut être converti implicitement en toute sécuritévoid *
, y compris les pointeurs vers les types de base, les classes sans table virtuelle et les classes avec table virtuelle.Puisqu'il doit être compatible avec toutes ces catégories d'objets,
void
il ne peut lui - même contenir de méthodes virtuelles. Sans fonctions virtuelles et RTTI, aucune information utile sur le type ne peut être obtenuevoid
(elle correspond à TOUS les types, donc ne peut dire que des choses qui sont vraies pour TOUS les types), mais les fonctions virtuelles et RTTI rendraient les types simples très inefficaces et empêcheraient C ++ d'être un langage adapté à la programmation de bas niveau avec accès direct à la mémoire, etc.Donc, il existe un tel type. Il fournit juste une interface très minimaliste (en fait, vide) en raison de la nature de bas niveau du langage. :-)
la source
void
.void
elle ne se compilera pas et vous obtenez cette erreur . En Java, vous pourriez avoir une variable de typeObject
et cela fonctionnerait. C'est la différence entrevoid
et un type «réel». Il est peut-être vrai quevoid
c'est le type de base pour tout, mais comme il ne fournit aucun constructeur, méthode ou champ, il n'y a aucun moyen de dire qu'il est là ou non. Cette affirmation ne peut être ni prouvée ni réfutée.void
c'est un type (et j'ai découvert une utilisation? :
un peu intelligente de ), mais il est encore loin d'être une classe de base universelle. D'une part, c'est «un type incomplet qui ne peut pas être complété», d'autre part, il n'y a pas devoid&
type.C ++ est un langage fortement typé. Pourtant, il est surprenant qu'il n'ait pas de type d'objet universel dans le contexte de la spécialisation des modèles.
Prenons, par exemple, le modèle
template <class T> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...)> { ... ReturnType operator () (ArgTypes... args) { ... } };
qui peut être instancié comme
Hook<decltype(some_function)> ...;
Supposons maintenant que nous voulons la même chose pour une fonction particulière. Comme
template <auto fallback> class Hook; template <auto fallback, class ReturnType, class ... ArgTypes> class Hook<ReturnType fallback(ArgTypes...)> { ... ReturnType operator () (ArgTypes... args) { ... } };
avec l'instanciation spécialisée
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 d'équivalent
auto fallback
(j'utilise cette syntaxe comme la syntaxe non-type générique la plus évidente dans ce contexte) qui pourrait remplacer tout argument de modèle non-type avant la spécialisation.Donc, en général, ce modèle ne transfère pas des arguments de modèle de type à des arguments de modèle non-type.
Comme avec beaucoup de coins en langage C ++, la réponse est probablement "aucun membre du comité n'y a pensé".
la source
ReturnType fallback(ArgTypes...)
travail fonctionnait, ce serait une mauvaise conception.template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...
fait ce que vous voulez et signifie que les paramètres du modèleHook
sont de natureC ++ a été initialement appelé "C avec classes". C'est une progression du langage C, contrairement à d'autres choses plus modernes comme C #. Et vous ne pouvez pas voir le C ++ comme un langage, mais comme une base de langages (Oui, je me souviens du livre de Scott Meyers, Effective C ++).
C lui-même est un mélange de langages, le langage de programmation C et son préprocesseur.
C ++ ajoute un autre mélange:
l'approche classe / objets
modèles
la STL
Personnellement, je n'aime pas certaines choses qui viennent directement du C au C ++. Un exemple est la fonctionnalité enum. La façon dont C # permet au développeur de l'utiliser est bien meilleure: il limite l'énumération dans sa propre portée, il a une propriété Count et il est facilement itérable.
Comme C ++ voulait être rétrocompatible avec C, le concepteur a été très permissif pour permettre au langage C d'entrer dans son ensemble en C ++ (il y a quelques différences subtiles, mais je ne me souviens de rien que vous pourriez faire en utilisant un compilateur C que vous ne pouvait pas utiliser un compilateur C ++).
la source