Accueil > > > PERFORMANCE : I++ OU ++I ?
PERFORMANCE : I++ OU ++I ?
Information sur la source
Description
Bien sûr, il y a parfois des raisons pour choisir i++ plutôt que ++i mais vous êtes vous posé la question de la performance ? Surtout lorsque l'on utilise cette construction à répétition ... dans une boucle par exemple. Alors : votez vous pour i++ (comme la majorité des développeurs), pour ++i (la minorité) ou pensez vous que c'est équivalent (ne sait pas) ? Voyons cela de l'intérieur : à l'occasion de la surcharge des opérateurs ++
Source
- /*
- On vous donne la classe A suivante :
- class A
- {
- private: int i_;
- public:
- A(int i) : i_(i){}
- void print() const {cout << "valeur A=" << i_ << endl;}
- };
- On vous demande de supporter le "Use Case" suivant :
-
- int main(){
- A a(0);
- a++.print();
- (++a).print();
- }
-
- 0) Question préliminaire :
- ---------------------------
- Quelle est l'arité de ++ dans a++ ? et dans ++a ?
- (l'arité d'un opérateur est son nombre de paramètres)
- la réponse dans les deux cas devrait être UN
- Donc, en l'implémentant comme méthode de la classe A, et en tenant compte
- du paramètre caché 'this', elle ne doit avoir aucun paramètre
- Comment différencier alors le cas a++ du cas ++a ?
- Les standards répondent à ce problème en imposant un paramètre 'int'
- à la version postfixée
-
- I) Commençons par le premier opérateur :
- -----------------------------------------
- l'implémentation de la version postfixée de l'opérateur ++
- Quel est le prototype ?
- Ecrivons la version complète de l'opérateur (sa vision compilateur)
- a++ est équivalent à a.operator++(int)
- Que doit faire cet opérateur ?
- d'abord incrémenter 'a', ensuite renvoyer l'ancienne valeur de 'a'
- On remarquera qu'ici, il est "NECESSAIRE DE CREER UN OBJET TEMPORAIRE"
- voici notre implémentation comme méthode de la classe A:
- ?A? operator++(int)
- { A clone(*this); //ceci est un appel au Ctor de copie de A
- //permet de mémoriser l'ancienne valeur
- ++i_; //incrémente l'objet courant
- return clone; //renvoie l'ancienne valeur
- } // ici appel au "DTOR DE L'OBJET TEMPORAIRE"
-
-
- où ?A? représente soit 'A', 'const A', 'A&' ou 'const A&'
- On élimine les pointeurs car le résultat de a++ est forcément un espèce de A
- Comment choisir ?
- Eliminons les références car l'objet renvoyé est temporaire (créé sur la pile)
- il disparait dès la sortie du bloc '}', il est "NECESSAIRE DE RENVOYER UNE COPIE"
- J'espère maintenant que vous êtes dégoutés du POST-FIXE !
-
- Pour départager les ex-aequos (A et const A) voyons comment se comporte
- l'opérateur des types de base (l'int par exemple)
- Testons le code suivant :
- int i(0); i++++;
- Une erreur de compilation survient car l'opérateur ++ postfixé des 'int'
- attend une L-VALUE (Left-value).
- L-VALUE : variable possédant une zone de mémoire accessible en lecture/écriture
- donc en particulier, pas une variable temporaire, ni une const.
- En résumé, une variable pouvant être placée à gauche du signe =
- Ceci nous donne notre réponse :
- le type de retour DOIT être un 'const A' plutôt que 'A' pour être conforme au standards
-
- On peut alors tester le premier opérateur ainsi
- int main()
- {
- int i(0);
- //i++++; ne compile pas ++ requiert l-value
- A a(0);
- //a++++; // ne compile pas si operator++(int) retourne un const
- cout << "valeur i=" << i++ << endl;//renvoie 0
- cout << "valeur i=" << i << endl;//renvoie 1
- a++.print();//renvoie 0 : attention à bien prototyper la méthode print const
- a.print();//renvoie 1
-
- return 0;
- }
-
- II) Deuxième opérateur :
- -----------------------------------------
- Quel est le prototype de la version préfixée de l'opérateur ++ ?
- ++a est équivalent à a.operator++()
- Que doit faire cet opérateur ?
- d'abord incrémenter 'a', ensuite renvoyer la nouvelle valeur de 'a'
- On remarquera qu'ici, que l'on n'a pas besoin d'objet temporaire (COOL)
-
- voici notre implémentation comme méthode de la classe A:
- ?A? operator++()
- { ++i_; //incrémente l'objet courant
- return *this; //le renvoie
- }
- On peut renvoyer un 'A', 'const A', 'A&' ou 'const A&'
- Le meilleur pour les performances est de renvoyer par référence
- Voyons si elle doit être const ou non avec la même technique que précédemment
- on teste
- ++++i; ... et ça marche !
- On peut donc retourner un A& (et c'est préférable pour imiter les types de base)
-
- */
-
- #include <iostream.h>
- class A
- {
- private: int i_;
- public: A(int i) : i_(i){}
- void print() const {cout << "valeur A=" << i_ << endl;}
- const A operator++(int)
- { A clone(*this); //ceci est un appel au Ctor de copie de
- //permet de mémoriser l'ancienne valeur
- ++i_; //incrémente l'objet courant
- return clone; //renvoie l'ancienne valeur
- }
- A& operator++()
- { ++i_; //incrémente l'objet courant
- return *this; //le renvoie
- }
- };
-
- int main()
- {
- int i(0);
- //i++++; ne compile pas ++ requiert l-value
- A a(0);
- //a++++; // ne compile pas si operator++(int) retourne un const
-
- //test post fixé
- cout << "valeur i=" << i++ << endl;//renvoie 0
- cout << "valeur i=" << i << endl;//renvoie 1
- a++.print();//=> 0 : attention à bien prototyper la méthode print const
- a.print();//=> 1
-
- //test pré fixé
- cout << "valeur i=" << ++i << endl;//renvoie 2
- cout << "valeur i=" << i << endl;//renvoie 2
- (++a).print(); //=> 2 //parenthèses nécessaires (précédence de . sur ++)
- a.print(); //=> 2
- return 0;
- }
-
- /* "CONCLUSION":
- Faisons le bilan :
- en notation "POST-FIXE" :
- un Ctor pour l'objet temporaire (Ctor de Copie ou Ctor défaut+Affectation)
- l'incrémentation
- un Ctor de Copie pour le retour par valeur
- un Dtor de l'objet temporaire
-
- en notation "PRE-FIXE" :
- l'incrémentation
-
- Un dernier mot pour dire que les types de bases se comportent de la même
- manière que les classes (une temporaire), même si les dégâts sont moins flagrants
- "il n'y a pas de différence dans le cas d'int" (merci aux commentaires ci-dessous . le 04/11)
- Et si notre classe avait des données membres de type classe, et/ou des
- classes de bases ... le poids des Ctor/Dtors devient ENORME...
- */
-
/*
On vous donne la classe A suivante :
class A
{
private: int i_;
public:
A(int i) : i_(i){}
void print() const {cout << "valeur A=" << i_ << endl;}
};
On vous demande de supporter le "Use Case" suivant :
int main(){
A a(0);
a++.print();
(++a).print();
}
0) Question préliminaire :
---------------------------
Quelle est l'arité de ++ dans a++ ? et dans ++a ?
(l'arité d'un opérateur est son nombre de paramètres)
la réponse dans les deux cas devrait être UN
Donc, en l'implémentant comme méthode de la classe A, et en tenant compte
du paramètre caché 'this', elle ne doit avoir aucun paramètre
Comment différencier alors le cas a++ du cas ++a ?
Les standards répondent à ce problème en imposant un paramètre 'int'
à la version postfixée
I) Commençons par le premier opérateur :
-----------------------------------------
l'implémentation de la version postfixée de l'opérateur ++
Quel est le prototype ?
Ecrivons la version complète de l'opérateur (sa vision compilateur)
a++ est équivalent à a.operator++(int)
Que doit faire cet opérateur ?
d'abord incrémenter 'a', ensuite renvoyer l'ancienne valeur de 'a'
On remarquera qu'ici, il est "NECESSAIRE DE CREER UN OBJET TEMPORAIRE"
voici notre implémentation comme méthode de la classe A:
?A? operator++(int)
{ A clone(*this); //ceci est un appel au Ctor de copie de A
//permet de mémoriser l'ancienne valeur
++i_; //incrémente l'objet courant
return clone; //renvoie l'ancienne valeur
} // ici appel au "DTOR DE L'OBJET TEMPORAIRE"
où ?A? représente soit 'A', 'const A', 'A&' ou 'const A&'
On élimine les pointeurs car le résultat de a++ est forcément un espèce de A
Comment choisir ?
Eliminons les références car l'objet renvoyé est temporaire (créé sur la pile)
il disparait dès la sortie du bloc '}', il est "NECESSAIRE DE RENVOYER UNE COPIE"
J'espère maintenant que vous êtes dégoutés du POST-FIXE !
Pour départager les ex-aequos (A et const A) voyons comment se comporte
l'opérateur des types de base (l'int par exemple)
Testons le code suivant :
int i(0); i++++;
Une erreur de compilation survient car l'opérateur ++ postfixé des 'int'
attend une L-VALUE (Left-value).
L-VALUE : variable possédant une zone de mémoire accessible en lecture/écriture
donc en particulier, pas une variable temporaire, ni une const.
En résumé, une variable pouvant être placée à gauche du signe =
Ceci nous donne notre réponse :
le type de retour DOIT être un 'const A' plutôt que 'A' pour être conforme au standards
On peut alors tester le premier opérateur ainsi
int main()
{
int i(0);
//i++++; ne compile pas ++ requiert l-value
A a(0);
//a++++; // ne compile pas si operator++(int) retourne un const
cout << "valeur i=" << i++ << endl;//renvoie 0
cout << "valeur i=" << i << endl;//renvoie 1
a++.print();//renvoie 0 : attention à bien prototyper la méthode print const
a.print();//renvoie 1
return 0;
}
II) Deuxième opérateur :
-----------------------------------------
Quel est le prototype de la version préfixée de l'opérateur ++ ?
++a est équivalent à a.operator++()
Que doit faire cet opérateur ?
d'abord incrémenter 'a', ensuite renvoyer la nouvelle valeur de 'a'
On remarquera qu'ici, que l'on n'a pas besoin d'objet temporaire (COOL)
voici notre implémentation comme méthode de la classe A:
?A? operator++()
{ ++i_; //incrémente l'objet courant
return *this; //le renvoie
}
On peut renvoyer un 'A', 'const A', 'A&' ou 'const A&'
Le meilleur pour les performances est de renvoyer par référence
Voyons si elle doit être const ou non avec la même technique que précédemment
on teste
++++i; ... et ça marche !
On peut donc retourner un A& (et c'est préférable pour imiter les types de base)
*/
#include <iostream.h>
class A
{
private: int i_;
public: A(int i) : i_(i){}
void print() const {cout << "valeur A=" << i_ << endl;}
const A operator++(int)
{ A clone(*this); //ceci est un appel au Ctor de copie de
//permet de mémoriser l'ancienne valeur
++i_; //incrémente l'objet courant
return clone; //renvoie l'ancienne valeur
}
A& operator++()
{ ++i_; //incrémente l'objet courant
return *this; //le renvoie
}
};
int main()
{
int i(0);
//i++++; ne compile pas ++ requiert l-value
A a(0);
//a++++; // ne compile pas si operator++(int) retourne un const
//test post fixé
cout << "valeur i=" << i++ << endl;//renvoie 0
cout << "valeur i=" << i << endl;//renvoie 1
a++.print();//=> 0 : attention à bien prototyper la méthode print const
a.print();//=> 1
//test pré fixé
cout << "valeur i=" << ++i << endl;//renvoie 2
cout << "valeur i=" << i << endl;//renvoie 2
(++a).print(); //=> 2 //parenthèses nécessaires (précédence de . sur ++)
a.print(); //=> 2
return 0;
}
/* "CONCLUSION":
Faisons le bilan :
en notation "POST-FIXE" :
un Ctor pour l'objet temporaire (Ctor de Copie ou Ctor défaut+Affectation)
l'incrémentation
un Ctor de Copie pour le retour par valeur
un Dtor de l'objet temporaire
en notation "PRE-FIXE" :
l'incrémentation
Un dernier mot pour dire que les types de bases se comportent de la même
manière que les classes (une temporaire), même si les dégâts sont moins flagrants
"il n'y a pas de différence dans le cas d'int" (merci aux commentaires ci-dessous . le 04/11)
Et si notre classe avait des données membres de type classe, et/ou des
classes de bases ... le poids des Ctor/Dtors devient ENORME...
*/
Sources du même auteur
Sources de la même categorie
Commentaires et avis
|
Derniers Blogs
ARTICLE DANS PROGRAMMEZ SUR LES PRINCIPES SOLIDARTICLE DANS PROGRAMMEZ SUR LES PRINCIPES SOLID par fathi
Hello tout le monde! J'ai pas pu blogger ces derniers temps car j'ai eu un (heureux) petit chamboulement dans ma vie perso (un "bug" de 3.8 kg et de 52 cm) J'en profite juste pour vous annoncer la parution d'un article sur les principes SOLID ...
Cliquez pour lire la suite de l'article par fathi PARUTION DE MON LIVRE SUR WPF 4PARUTION DE MON LIVRE SUR WPF 4 par odewit
La 2e édition de mon livre sur WPF sort aujourd'hui en version numérique et lundi en version papier :-)
L'ouvrage présente de façon approfondie les fonctionnalités de WPF 4 : graphisme 2D et 3D, animation, multimédia, interfaces utilisateur, databind...
Cliquez pour lire la suite de l'article par odewit EDM : COMMENT UTILISER L'HORIZONTAL ENTITY SPLITTINGEDM : COMMENT UTILISER L'HORIZONTAL ENTITY SPLITTING par Matthieu MEZIL
Une des raisons pour lesquelles j'adore l'Entity Framework est la puissance de son mapping. Beaucoup de développeurs pour ne pas dire la plus part n'en n'ont pas conscience. Pour rappel, j'ai réalisé des videos (en anglais) sur le mapping . Certains scena...
Cliquez pour lire la suite de l'article par Matthieu MEZIL [WP7DEV][REACTIVE] RENDRE LES REACTIVE EXTENSIONS PLUS STABLES[WP7DEV][REACTIVE] RENDRE LES REACTIVE EXTENSIONS PLUS STABLES par jay
Lorsque l'on développe des applications .NET, les exceptions non gérées dans des threads ont le désagréable effet de terminer le processus courant.
Dans l'exemple suivant.......(read more) ...
Cliquez pour lire la suite de l'article par jay
Forum
DE L'AIIIDE!!DE L'AIIIDE!! par eliramomo
Cliquez pour lire la suite par eliramomo
Logiciels
Microsoft Office (2010)MICROSOFT OFFICE (2010)Microsoft Office 2010 offre de nouveaux moyens flexibles et puissants pour optimiser votre travai... Cliquez pour télécharger Microsoft Office SeaMonkey (2.0.7)SEAMONKEY (2.0.7)Le projet SeaMonkey est issu d'un effort communautaire pour developper une application tout en un... Cliquez pour télécharger SeaMonkey Safari (5.0.2)SAFARI (5.0.2)Le navigateur d'Apple a lui aussi été mis à jour, aussi bien dans sa mouture Windows que celle po... Cliquez pour télécharger Safari Mozilla FireFox (4.0 béta 5)MOZILLA FIREFOX (4.0 BéTA 5)Firefox 4.0 béta 5
L'une des nouveautés visibles les plus attendues réside sans doute dans l'a... Cliquez pour télécharger Mozilla FireFox Mozilla Firefox (3.6.9)MOZILLA FIREFOX (3.6.9)Firefox 3.6.9 corrige les problèmes suivants :
* Introduced support for the X-FRAME-OPTION... Cliquez pour télécharger Mozilla Firefox
|