Nous avons regardé cette réponse pour C dans cette question:
http://stackoverflow.com/questions/24886/is-there-a-performance-difference-between-i-and-i-in-c
Quelle est la réponse pour C ++?
Nous avons regardé cette réponse pour C dans cette question:
http://stackoverflow.com/questions/24886/is-there-a-performance-difference-between-i-and-i-in-c
Quelle est la réponse pour C ++?
[Résumé: Utilisez ++i
si vous n'avez pas de raison particulière d'utiliser i++
.]
Pour C++, la réponse est un peu plus compliqué.
Si i
est un type simple (pas une instance d'une classe C++), alors la réponse donnée pour C est titulaire, depuis le compilateur génère le code.
Toutefois, si i
est une instance d'une classe C++, alors i++
et ++i
sont des appels vers l'un de l' operator++
fonctions. Voici une norme paire de ces fonctions:
Foo& Foo::operator++() // called for ++i
{
this->data += 1;
return *this;
}
Foo Foo::operator++(int ignored_dummy_value) // called for i++
{
Foo tmp(*this); // variable "tmp" cannot be optimized away by the compiler
++(*this);
return tmp;
}
Car le compilateur n'est pas de la génération de code, mais juste de l'appel d'un operator++
de la fonction, il n'existe aucun moyen pour optimiser loin l' tmp
variable et de ses associés constructeur de copie. Si le constructeur de copie est cher, alors cela peut avoir un impact significatif sur les performances.
(Merci à Paul pour s'enquérir au sujet de la différence entre le C et le C++.)
Oui. Il y est.
Le ++ opérateur peut ou ne peut pas être définie comme une fonction. Pour les types primitifs (int, double,...), les exploitants se sont intégrés, de sorte que le compilateur sera probablement en mesure d'optimiser votre code. Mais dans le cas d'un objet qui définit l'opérateur ++ les choses sont différentes.
L'opérateur++(int) de la fonction de doit créer une copie. C'est parce que postfix ++ est prévu pour renvoyer une valeur différente de celle qu'il détient: il doit tenir sa valeur dans une variable temp, incrémenter sa valeur et le retour de la temp. Dans le cas de l'opérateur++(), préfixe ++, il n'est pas nécessaire de créer une copie: l'objet peut incrément de lui-même et ensuite il suffit de retourner lui-même.
Voici une illustration de ce point:
struct C
{
C& operator++(); // prefix
C operator++(int); // postfix
private:
int i_;
};
C& C::operator++()
{
++i_;
return *this; // self, no copy created
}
C C::operator++(int ignored_dummy_value)
{
C t(*this);
++(*this);
return t; // return a copy
}
Chaque fois que vous appelez l'opérateur++(int), vous devez créer une copie, et le compilateur ne peut pas faire quoi que ce soit. Quand donné le choix, l'utilisation de l'opérateur++(); de cette façon, vous n'avez pas enregistrer une copie. Il pourrait être important dans le cas de beaucoup d'incréments (grande boucle?) et/ou d'objets volumineux.
Voici un indice de référence pour le cas lors de l'incrémentation des opérateurs dans les différentes unités de traduction. Compilateur g++ 4.5.
Ignorer les questions de style pour le moment
// a.cc
#include <ctime>
#include <array>
class Something {
public:
Something& operator++();
Something operator++(int);
private:
std::array<int,PACKET_SIZE> data;
};
int main () {
Something s;
for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
std::clock_t a = clock();
for (int i=0; i<1024*1024*30; ++i) ++s;
a = clock() - a;
for (int i=0; i<1024*1024*30; ++i) s++; // warm up
std::clock_t b = clock();
for (int i=0; i<1024*1024*30; ++i) s++;
b = clock() - b;
std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
<< ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
return 0;
}
// b.cc
#include <array>
class Something {
public:
Something& operator++();
Something operator++(int);
private:
std::array<int,PACKET_SIZE> data;
};
Something& Something::operator++()
{
for (auto it=data.begin(), end=data.end(); it!=end; ++it)
++*it;
return *this;
}
Something Something::operator++(int)
{
Something ret = *this;
++*this;
return ret;
}
Les résultats (horaires sont en secondes) avec g++ 4.5 sur une machine virtuelle:
Flags (--std=c++0x) ++i i++
-DPACKET_SIZE=50 -O1 1.70 2.39
-DPACKET_SIZE=50 -O3 0.59 1.00
-DPACKET_SIZE=500 -O1 10.51 13.28
-DPACKET_SIZE=500 -O3 4.28 6.82
Prenons maintenant le fichier suivant:
// c.cc
#include <array>
class Something {
public:
Something& operator++();
Something operator++(int);
private:
std::array<int,PACKET_SIZE> data;
};
Something& Something::operator++()
{
return *this;
}
Something Something::operator++(int)
{
Something ret = *this;
++*this;
return ret;
}
Elle ne fait rien dans l'incrémentation. Cela simule le cas lors de l'incrémentation a constante de la complexité.
Résultats sont très variables:
Flags (--std=c++0x) ++i i++
-DPACKET_SIZE=50 -O1 0.05 0.74
-DPACKET_SIZE=50 -O3 0.08 0.97
-DPACKET_SIZE=500 -O1 0.05 2.79
-DPACKET_SIZE=500 -O3 0.08 2.18
-DPACKET_SIZE=5000 -O3 0.07 21.90
Si vous n'avez pas besoin de la valeur précédente, prenez l'habitude d'utiliser les pré-incrémentation. Être compatible même avec builtin types, vous allez en prendre l'habitude et ne pas courir le risque de la souffrance inutile perte de performances si vous remplacez un builtin type avec un type personnalisé.
i++
, dit - increment i, I am interested in the previous value, though
.++i
, dit - increment i, I am interested in the current value
ou increment i, no interest in the previous value
. Encore une fois, vous allez en prendre l'habitude, même si vous n'êtes pas maintenant.L'optimisation prématurée est la racine de tous les maux. Comme il est prématuré pessimization.
Ce n'est pas tout à fait correct de dire que le compilateur ne peut pas optimiser loin la variable temporaire copie dans le postfix cas. Un test rapide avec VC montre que, au moins, peut le faire dans certains cas.
Dans l'exemple suivant, le code généré est identique pour le préfixe et le suffixe, par exemple:
#include <stdio.h>
class Foo
{
public:
Foo() { myData=0; }
Foo(const Foo &rhs) { myData=rhs.myData; }
const Foo& operator++()
{
this->myData++;
return *this;
}
const Foo operator++(int)
{
Foo tmp(*this);
this->myData++;
return tmp;
}
int GetData() { return myData; }
private:
int myData;
};
int main(int argc, char* argv[])
{
Foo testFoo;
int count;
printf("Enter loop count: ");
scanf("%d", &count);
for(int i=0; i<count; i++)
{
testFoo++;
}
printf("Value: %d\n", testFoo.GetData());
}
Si vous n' ++testFoo ou testFoo++, vous aurez toujours le même code. En fait, sans lire le comte de l'utilisateur, l'optimiseur a obtenu l'ensemble de la chose vers le bas à une constante. Donc ceci:
for(int i=0; i<10; i++)
{
testFoo++;
}
printf("Value: %d\n", testFoo.GetData());
A abouti à la suivante:
00401000 push 0Ah
00401002 push offset string "Value: %d\n" (402104h)
00401007 call dword ptr [__imp__printf (4020A0h)]
Ainsi, alors qu'il est certainement le cas que postfix version pourrait être plus lent, il se pourrait bien que l'optimiseur va être assez bon pour se débarrasser de la copie temporaire si vous ne l'utilisez pas.
Il y a quelque chose à ce sujet sur Google c ++ document style de codage.
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.