Les avantages de la sémantique par référence: flexibilité et liaison dynamique (dynamic binding) (vous avez la liaison dynamique en C++ seulement quand vous utilisé des pointeurs ou des références pas quand vous utilisez des valeurs).
Les avatages de la sémantique par valeur: vitesse d'exécution. "vitesse" semble un curieux bénéfice pour une caractéristiques qui requiert la copie d'un objet (vs. la copie d'un un pointeur), mais le fait est qu'habituellement on accède plus souvent a un objet qu'on ne le copie, ainsi le coût de copie occasionnel est (généralement) plus que masqué par le bénéfice d'accéder directement a l'objet plutôt qu'a travers un pointeur vers l'objet.
Il y a trois cas ou nous avons un objet plutôt qu'un pointeur vers un objet: : objet local, objet global/statique, et membre objet d'une classe. Le plus important des trois est le dernier ("composition").
Plus d'informations a propos des valeurs contre références sont donnés dans les FAQ suivantes. S'il-vous-plait lisez-les toutes sinon vous n'aurez pas une perspective juste. Les premières FAQ sont intentionnellement biaisé en faveur de l'utilisation de la sémantique par valeur, aussi si vous lisez seulement les premières FAQ de cette section votre perspective du problème sera déformée.
L'assignation possède aussi d'autre subtilités (e.g., copie superficielle contre copie en profondeur) qui ne sont pas couverte ici.
[ Haut | Bas | Rechercher ]
Pour simuler une donnée virtuelle en C++, la classe de base doit posséder un pointeur vers l'objet membre, et la classe dérivée doit fournir un objet alloué via new qui sera pointé par le pointeur membre de la classe de base. La classe de base pourrait être munie d'un ou plusieurs constructeur normaux qui fournissent leur propre pointeur (a nouveau via new), et le destructeur de la classe de base devrait détruire le pointeur via delete.
Par exemple, la class Pile peut avoir un objet Tableau membre (en utilisant un pointeur), et la class dérivée PileAgrandissable peut surcharger le membre de la class de base de Tableau à TableauAgrandissable. Pour que ceci fonctionne, TableauAgrandissable doit être dérivé de Tableau, ainsi Pile possède un Tableau*. Le constructeur par défaut de Pile initialise ce pointeur avec un Tableau* via un new Tableau, mais Pile doit aussi fournir une constructeur (éventuellement protégé:) qui reçoit en paramètres un Tableau* provenant d'une class dérivé. Le constructeur de TableauAgrandissable's fournira un new TableauAgrandissable a ce constructeur spécial.
Avantages:
S'il vous plaît lisez le reste de cette section. (Vous n'aurez pas une vue objective sans les autres.)
[ Haut | Bas | Rechercher ]
Maintenant revenez en arrière et relisez la paragraphe précédent en faisant les substitutions:
Une autre façon de considérer ceci est de distinguer les fonctions membres "par objet" des fonctions membres "dynamique". Une fonction "par objet" est une fonction membre qui est potentiellement différente pour chaque instance d'un objet et peut-être implémenté par un pointeur de fonction dans l'objet; ce pointeur doit être const, car le pointeur ne changera pas durant la durée de vie de l'objet. Une fonction membre "dynamique" est une fonction membre qui peut changer dynamiquement dans le temps; ceci peut aussi être implémenté par un pointeur de fonction mais le pointeur ne peut pas être const.
En étendant l'analogie, ceci nous donne trois concepts distincts pour les données membres:
[ Haut | Bas | Rechercher ]
Vos objet membres devrait normalement être "contenu" dans l'objet composite (mais pas toujours; les objets "enveloppe" sont un bon exemple ou vous devrez utilisé des pointeur/référence; les relations N-to-1-uses-a (N objets utilisent un objet commun) nécessitent aussi un pointeur/référence).
Il y a trois raisons pour lesquelles des objets membres pleinement contenus ("composition") présente de meilleur performance que des pointeurs vers des objets membres alloués:
[ Haut | Bas | Rechercher ]
Note: Lisez les trois FAQ suivantes pour obtenir un vision équilibré du phénomène!
[ Haut | Bas | Rechercher ]
Quand un objet est utilisé via une référence ou un pointeur, un appel a une fonction virtual ne peut pas être mise en ligne, car l'appel doit être résolu dynamiquement. Raison: le compilateur ne peut pas connaître le code à appeler avant l'exécution (i.e., dynamiquement), car le code peut être fourni par un classe dérivée qui a été crée après que l'appelant ait été compilé.
Aussi la seul fois où une fonction inline virtual peut-être mise en ligne est lorsque le compilateur connaît le type "exact de la classe" de l'objet qui est la cible de l'appel la fonction virtual. Ceci ne peut arriver que si le compilateur manipule un objet plutôt qu'un pointeur ou un référence vers un objet. I.e., soit avec un objet local, un objet global/statique, ou un objet contenu dans une classe.
Notez que la différence de surcoût en vitesse entre un fonction mise en ligne et non mise en ligne est normalement beaucoup plus significatif que la différence entre un appel de fonction "normal" et un appel de fonction virtual. Par exemple, la différence entre un appel de fonction "normal" et un appel de fonction virtual est souvent de deux références mémoires, mais le différence entre une fonction en ligne et une fonction non en ligne peut aller jusqu'à un ordre de grandeur (pour des zillions d'appels a des fonctions trivial, la perte de la mise en ligne de fonction virtuel peut résulter en un dégradation de la vitesse par 25 ! [Doug Lea, "Customization in C++," proc Usenix C++ 1990]).
Une conséquence pratique de ceci: ne vous laissez pas leurrer par des débats sans fin (ou des tactiques de ventes!) de vendeurs de compilateur/langage qui comparent le coût d'un appel de fonction virtual dans leur langage/compilateur avec un autre langage/compilateur. De telles comparaisons sont largement sans significations quand il est comparé avec la capacité du langage/compilateur de mettre en ligne les appels de fonctions. I.e., beaucoup de vendeurs d'implémentation de langage font beaucoup de bruit sur comment fonctionne leurs stratégies d'appels de fonctions virtuelles, mais si ces implémentations ne mettent pas en ligne les appels de fonctions les performances globales des programmes seront pauvres car c'est la mise en ligne pas la stratégie d'appel qui a le plus d'impact sur les performances
Note: Lisez les deux FAQ suivantes pour voir l'autre facette de ce point de vue!
[ Haut | Bas | Rechercher ]
La sémantique par références est Une Bonne Chose. Nous ne pouvons pas vivre sans pointeurs. Nous voulons seulement que nos logiciels ne soient pas de gigantesque nid de pointeurs. En C++, vous pouvez choisir ou vous voulez utiliser la sémantique par références (pointeurs/références) et où vous voulez la sémantique par valeurs (où les objets sont physiquement contenus pas d'autres objets etc.) Dans une grand système, cela devrait être un équilibré. Toutefois si vous implémentez absolument toute chose avec des pointeurs, vous aurez un fort surcoût à l'exécution.
Les objets proches du problèmes sont plus grand que les objets de haut niveau. L' identité de ces abstractions proches du problème sont habituellement plus importante que leur "valeur" Aussi la sémantique par références devrait-étre utilisée pour ces objets.
Notez que les objets dans l'espace du problème sont normalement a un plus haut niveau que les objets dans l'espace des solutions, donc les objets dans l'espace du problème ont normalement une fréquence plus basse d'interaction . Ainsi le C++ nous donne une situation idéalle : nous choisissons la sémantique par références pour les objets qui nécessite une identité unique ou qui sont trop grand pour être copié, et nous choisissons la sémantique par valeur pour les autres. Ainsi les objets avec une fréquence élevée d'utilisation auront une sémantique par valeur, car nous choisissons la flexibilité là ou elle n'entraînera que peu de surcoût, et nous aurons les performances là ou elles sont le plus nécessaires!
Ceci sont quelques unes des directions à prendre pour faire de la vrai conception OO. La maîtrise OO/C++ prend du temps et demande un entraînement de haute qualité. Si vous voulez un outil puissant vous devrez vous investir.
Ne vous arrêtez pas! Lisez la FAQ suivante aussi!!
[ Haut | Bas | Rechercher ]
La FAQ précédente discutait des objets membres, pas des paramètres. Généralement, les objets qui sont une partie d'une hiérarchie de classe devrait être passé par référence ou par pointeur, pas par valeur, ainsi et seulement ainsi vous aurez le comportement (désiré) de typage dynamique (la sémantique par valeur ne fonctionne pas bien avec l'héritage, car les objets de classe parente sont "émincés (N.D.T sliced)" quand il sont passés par valeur comme une classe de base).
A moins de contraintes supplémentaires les objets membres devraient utiliser la sémantique par valeur et les paramètres la sémantique par référence. Les discussions dans les FAQ précédentes donne quelques une des "contraintes supplémentaires" pour lesquelles des objets membres devraient être inclus par référence.
[ Haut | Bas | Rechercher ]
Ecrire à l'auteur,
au traducteur,
ou en savoir plus sur la traduction.
C++ FAQ Lite fr |
Table des matières |
Index |
A propos de l'auteur |
© |
Téléchargez votre propre copie ]
Dernière révision Sun Apr 13 23:54:39 PDT 2003