2010-07-05 3 views
19

Je veux m'assurer que je comprends bien la valeur de passage par rapport à la référence de passage. En particulier, je regarde les versions de préfixe/postfix de l'opérateur d'incrément ++ pour un objet.Opérateurs d'incrémentation préfixes/postfixes

Supposons que nous avons la classe X suivante:

class X{ 
private: 
    int i; 
public: 
X(){i=0;} 
X& operator ++(){ ++i; return *this; } //prefix increment 

X operator ++ (int unused){ //postfix increment 
    X ret(*this); 
    i++; 
    return ret; 
} 

operator int(){ return i; } //int cast 
}; 

D'abord, ai-je mis en œuvre le préfixe/opérateurs d'incrément postfix correctement?

Deuxièmement, comment l'opérateur postfix est-il efficace en mémoire, comparé à l'opérateur de préfixe? Plus précisément, combien de copies d'objet X sont créées lorsque chaque version de l'opérateur est utilisée?

Une explication de ce qui se passe exactement avec le retour par référence et le retour par valeur pourrait m'aider à comprendre.


Edit: Par exemple, le code suivant ...

X a; 
X b=a++; 

... sont a et b maintenant des alias?

+0

Il n'est pas nécessaire de post-incrémenter 'i' dans l'opérateur postfix. En fait, je ferais comme [FredOverflow suggère] (http://stackoverflow.com/questions/3181211/3181359#3181359) et appelez la version de préfixe. OMI est en effet plus idiomatique que de ré-implémenter l'incrément (même si la mise en œuvre est triviale ici). Et débarrassez-vous de cet opérateur de conversion implicite. Cela va vous faire du mal autrement. (La troisième et dernière fois que j'ai écrit un opérateur de conversion implicite était en 2001 et un ou deux ans plus tard, j'ai découvert qu'il causait des bugs subtils et l'a supprimé - comme tous les précédents.) – sbi

Répondre

17

Cette implémentation est correcte. Il est typique qu'un opérateur de postfix soit moins performant car il faut créer une autre copie avant de faire l'incrément (et c'est pourquoi j'ai pris l'habitude de toujours utiliser le préfixe sauf si j'ai besoin d'autre chose).

Avec retour par référence, vous renvoyez une référence de valeur l à l'objet actuel. Le compilateur implémenterait typiquement ceci en renvoyant l'adresse de l'objet actuel. Cela signifie que le retour de l'objet est aussi simple que de renvoyer un nombre. Toutefois, avec la valeur de retour, une copie doit être effectuée. Cela signifie qu'il y a plus d'informations à copier pendant le retour (au lieu d'une simple adresse) ainsi qu'un constructeur de copie à appeler. C'est là qu'intervient votre performance.

L'efficacité de votre implémentation est à la hauteur des implémentations typiques.

EDIT: En ce qui concerne votre addenda, non, ils ne sont pas des alias. Vous avez créé deux objets distincts. Lorsque vous revenez par valeur (et lorsque vous avez créé un nouvel objet à partir de l'opérateur d'incrément de postfix), ce nouvel objet est placé dans un emplacement mémoire distinct.

Cependant, dans le code suivant, a et b sont alias:

int a = 0; 
int& b = ++a; 

b est une adresse qui fait référence à un.

+2

Correct en général, modulo possible Optimisation (http://en.wikipedia.org/wiki/Return_value_optimization). –

2

Vos opérateurs sont implémentés correctement.

Dans l'opérateur de préfixe, aucune copie de X n'est effectuée.

Dans l'opérateur postfix, une copie est faite pour ret, et potentiellement une autre copie est faite au retour de la fonction, mais tous les compilateurs élideront cette copie.

15

Il est plus idiomatiques d'appeler l'incrément de préfixe de l'objet lui-même dans l'incrément postfix:

X operator++(int) 
{ 
    X copy(*this); 
    ++*this;   // call the prefix increment 
    return copy; 
} 

La logique de incrémenter un objet X est donc uniquement contenu dans la version préfixe.

+0

Oui, cela me libère de poster la même correction. '+ 1' de moi. – sbi