Performance : i++ ou ++i ?

Contenu du snippet

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 / Exemple :


/*
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...
  • /

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.