53 votes

Pourquoi ne pas avoir toutes les fonctions aussi virtuelles en C ++?

Je sais que les fonctions virtuelles ont une surcharge de déférence à l'appel d'une méthode. Mais je suppose qu'avec d'architecture moderne de vitesse, il est presque négligeable.

  1. Est-il une raison particulière à toutes les fonctions en C++ ne sont pas virtuels comme en Java?
  2. De mes connaissances, la définition d'une fonction virtuelle dans une classe de base est suffisante ou nécessaire. Maintenant, quand j'écris une classe parent, je ne pourrais pas savoir quelles méthodes passerait-monté. Donc, est-ce à dire que lors de l'écriture d'un enfant de la classe, quelqu'un l'aurait à modifier la classe parent. Cela sonne comme gênant et parfois pas possible?

Mise à jour:
En résumant de Jon Skeet la réponse ci-dessous:

C'est un compromis entre explicitement ce qui rend quelqu'un de réaliser qu'ils sont héritiers de la fonctionnalité [qui a le potentiel de risques en eux-mêmes [(vérification de Jon réponse)] [et le potentiel des petits gains de performance] avec un compromis pour les moins de souplesse, plus de changements de code, et plus raide de la courbe d'apprentissage.

D'autres raisons de réponses différentes:

Fonctions virtuelles ne peuvent pas être doublé car inline se produire lors de l'exécution. Cela a un impact sur les performances lorsque vous pensez que vous des fonctions des avantages de l'in-lining.

Il y pourrait y avoir d'autres raisons, et je voudrais bien les connaître et de les résumer.

76voto

Jon Skeet Points 692016

Il y a de bonnes raisons pour contrôler les méthodes sont virtuelles au-delà de la performance. Bien que je n'ai pas fait faire la plupart de mes méthodes de finale en Java, je devrais probablement... à moins qu'une méthode est conçue pour être remplacé, il ne devrait probablement pas être virtuel de l'OMI.

La conception de l'héritage peut être difficile - en particulier, cela signifie que vous devez document beaucoup plus sur ce qui pourrait l'appeler et de ce qu'il pourrait appeler. Imaginez si vous disposez de deux méthodes virtuelles, et l'un appelle l'autre - qui doit être documenté, sinon, quelqu'un pourrait remplacer le "appelée" méthode avec une mise en œuvre qui appelle la "vocation" de la méthode, sans le vouloir la création d'un débordement de la pile (ou boucle infinie si il y a la queue appel d'optimisation). À ce stade, vous avez alors obtenu moins de flexibilité de la mise en œuvre - vous ne pouvez pas passer à tour à une date ultérieure.

A noter que C# est un langage similaire à Java de différentes manières, mais ont choisi de faire des méthodes non-virtuel par défaut. Quelques autres personnes ne sont pas enthousiaste, mais j'ai certainement la bienvenue, et je préfères que les classes ont été uninheritable par défaut.

En gros, ça revient à ce conseil de Josh Bloch: la conception de l'héritage ou de l'interdire.

52voto

eran Points 12628
  1. L'un des principes principaux de C ++ est le suivant: vous ne payez que pour ce que vous utilisez ("principe de la surcharge zéro"). Si vous n'avez pas besoin du mécanisme de répartition dynamique, vous ne devriez pas payer pour ses frais généraux.

  2. En tant qu'auteur de la classe de base, vous devez décider quelles méthodes doivent être autorisées à être remplacées. Si vous écrivez les deux, allez-y et réfléchissez à ce dont vous avez besoin. Mais cela fonctionne de cette façon, car il doit exister un moyen pour l’auteur de la classe de base de contrôler son utilisation.

29voto

Konrad Rudolph Points 231505

Mais je suppose qu'avec d'architecture moderne de vitesse, il est presque négligeable.

Cette hypothèse est fausse, et, je suppose, la raison principale de cette décision.

Prenons le cas de l'in-lining. C++' sort fonction s'exécute beaucoup plus rapidement que C est le contraire similaire qsort dans certains scénarios, car il peut inline son comparateur argument, alors que C est impossible (en raison de l'utilisation de pointeurs de fonction). Dans les cas extrêmes, cela peut signifier des différences de rendement de plus de 700% (Scott Meyers, Efficace STL).

Il en serait de même pour les fonctions virtuelles. Nous avons eu des discussions similaires avant, par exemple, Est-il une raison d'utiliser le C++ au lieu de C, Perl, Python, etc?

14voto

La plupart des réponses à composer avec la surcharge de fonctions virtuelles, mais il y a d'autres raisons de ne pas le faire de toute fonction dans une classe virtuelle, comme le fait qu'il va changer la classe de standard-layout , eh bien, non-standard-layout, et qui peut être un problème si vous avez besoin de sérialiser des données binaires. Qu'est résolu différemment en C#, par exemple, en structs une autre famille de types de classes.

Du point de vue du design, chaque fonction publique établit un contrat entre le type et les utilisateurs de la nature, et chaque fonction virtuelle (public ou non) établit un autre contrat avec les classes qui étendent votre type. Plus le nombre de contrats que vous signez le moins de place pour les modifications que vous avez. Comme une question de fait, il y a très peu de gens, y compris certains bien connus des écrivains, que de défendre que l'interface publique ne doivent jamais contenir de fonctions virtuelles, comme votre compromis de vos clients peut être différent du compromis que vous exigez de vos extensions. Qui est, les interfaces publiques montre ce que vous faites pour vos clients, tandis que l'interface virtuelle montre comment les autres pourraient vous aider à le faire.

Un autre effet des fonctions virtuelles est qu'ils obtiennent toujours envoyés au final overrider (sauf si vous explicitement qualifier l'appel), et qui signifie que toute fonction qui est nécessaire pour maintenir votre invariants (pensez à l'état des variables privées) ne doit pas être virtuel: si une classe étend, il faudra faire explicitement qualifié d'appel à la maison mère ou d'autre serait briser le invariants à votre niveau.

Ceci est similaire à l'exemple de la boucle infinie/débordement de pile @Jon Skeet mentionné, seulement d'une manière différente: vous avez du document dans chaque fonction s'il accède à un privé attributs de sorte que les extensions de s'assurer que la fonction est appelée au bon moment. Et qui à son tour signifie que vous êtes en rupture d'encapsulation et vous avez une fuite d' abstraction: Vos détails internes sont maintenant une partie de l'interface (documentation + exigences de vos extensions), et vous ne pouvez pas les modifier comme vous le souhaitez.

Puis il y a la performance... il y aura un impact dans la performance, mais dans la plupart des cas, c'est surfait, et l'on peut dire que c'est seulement dans les rares cas où la performance est critique, vous tomber en arrière et déclarer les fonctions non-virtuel. Puis à nouveau, qui pourrait ne pas être simple sur un bâti de produits, depuis les deux interfaces (public + extensions) sont déjà liés.

8voto

roni Points 835

Tu oublies une chose. La surcharge est également en mémoire, c'est-à-dire que vous ajoutez une table virtuelle et un pointeur sur cette table pour chaque objet. Maintenant, si vous avez un objet qui a un nombre significatif d'instances attendu, il n'est pas négligeable. Par exemple, million d'instances équivaut à 4 méga octets. Je conviens que pour une application simple, ce n'est pas beaucoup, mais cela compte pour les périphériques en temps réel tels que les routeurs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X