2013-06-17 2 views
5
#include<iostream> 
using namespace std; 
class X 
{ 
    int i; 

    public: 
    X(int a=0) : i(a) {} 

    friend X operator+ (const X& left,const X&right); 

}; 
X operator+ (const X& left,const X&right) // Method 1 
{ 
    return X(left.i + right.i); 
} 

X operator+ (const X& left,const X&right) // Method 2 
{ 
    X temp(left.i + right.i); 
    return temp; 
} 

int main() 
{ 
    X a(2),b(3),c; 

    c=a+b; 

    c.print(); 
    return 0; 
} 

Dans ce code, l'opérateur + est surchargé par 2 méthodes différentes.Différence dans ces méthodes de surcharge d'opérateur en C++

Ma question est: Quelle est la différence entre ces méthodes et lesquelles devraient être considérées plus pratiques à utiliser?

+1

En C++ 11, 'return {left.i + right.i}' est la méthode # 3 qui ... fait aussi la même chose (après des optimisations triviales). – Yakk

Répondre

6

Je ne peux pas voir un cas où un compilateur génèrerait du code différent entre ces deux versions. La seconde est légèrement plus verbeuse, mais le compilateur est autorisé à optimiser la copie supplémentaire notionnelle dans ce cas et je ne suis pas au courant de tout compilateur qui ne ferait pas cette élision. Cela dit, il s'agit d'une micro-optimisation: Ecrire le code le plus clair, ce qui m'amène à mon dernier point. Ne pas écrire l'un de ces opérateurs, mais écrire la version idiomatiques conjointement avec +=:

X& operator+=(const X&right) { i += right.i; return *this; } 
X operator+(X left, const X& right) { return left += right; } 
+0

+1. @MichaelSmith: Je pense que vous devriez choisir cette réponse, elle vous donne la bonne suggestion sur la façon de concevoir vos opérateurs. –

+0

Si vous envisagez de créer votre code 'constexpr', écrivez' operator +', puis écrivez 'operator + =' en fonction de cela. – CTMacUser

3

Il n'y a pas de différence entre ces deux méthodes, et vous devriez utiliser celle qui communique le mieux son intention pour vous.

Paragraphe 12.8/31 sur copie élision spécifie:

Lorsque certains critères sont respectés, une mise en œuvre est autorisé à omettre la construction copier/déplacer d'un objet classe , même si le constructeur sélectionné l'opération de copie/déplacement et/ou le destructeur de l'objet ont des effets secondaires. Dans de tels cas, l'implémentation traite la source et la cible de l'opération comme étant simplement deux manières différentes de se référer au même objet, et la destruction de cet objet se produit à la fin des temps où les deux objets ont été détruits sans l'optimisation. Cette élision des opérations de copie/déplacement, appelé copie élision, est autorisée dans les circonstances suivantes (qui peuvent être combinés pour éliminer les copies multiples):

- dans un communiqué return en fonction d'un type de retour de classe lorsque l'expression est le nom d'un objet automatique non volatile (autre qu'un paramètre de fonction ou de clause catch) avec le même type cv-non qualifié que le type de retour de fonction, l'opération de copie/déplacement peut être omise en construisant l'objet automatique directement dans la valeur de retour de la fonction

- [...]

Comme vous pouvez le voir, à la fois la création d'un temporaire et un id expression nommant un objet local avec une durée de stockage automatique admissibles pour la copie élision.

De plus, le compilateur traitera la temp locale comme un rvalue (lire: comme temporaire, dans ce cas) aux fins de celui-ci revenant d'une fonction. Paragraphe 12.8/32 des 11 C++ Précise standard:

Lorsque les critères d'élision d'une opération de copie sont remplies ou seraient réunies, sauf pour le fait que la source objet est un paramètre de fonction, et la l'objet à copier est désigné par un lvalue, la résolution de surcharge à sélectionnez le constructeur pour la copie est d'abord effectuée comme si l'objet était désigné par une valeur. [...]

À cause de cela, je vous suggère fortement suppression la qualification const du type de retour:

const X operator + (const X& left, const X&right) 
// ^^^^^ 
// Don't use this! 

En C++ 11, cela va inhiber la sémantique de mouvement, parce que vous ne pouvez pas passer de un objet const, même s'il s'agit d'un rvalue - en bref, le constructeur de déplacement de X, à condition qu'il en existe un, ne soit pas sélectionné et le constructeur de copie sera appelé.

+0

n'est pas le premier à utiliser le concept de "RVO"? Corrigez-moi si je me trompe ? –

+0

Très probablement, il voulait faire la méthode const, pas l'objet de retour. –

+1

@ T.E.D .: Ce n'est pas une fonction membre, donc il ne peut pas être 'const' ... –

2

La différence est que la méthode 1 utilise un concept appelé "OPTIMISATION DE LA VALEUR RENVOYÉE".

Méthode 1:

Lorsque le compilateur voit que vous avez aucune utilité pour l'objet, il crée autre que de le renvoyer. Le compilateur en profite et "il construit l'objet directement à l'endroit où cette valeur est supposée être retournée". ici, un seul appel de constructeur ordinaire est nécessaire (aucun constructeur de copie n'est requis) et il n'y a pas d'appel de destructeur car vous n'avez jamais créé d'objet local. c'est plus efficace.


Méthode 2:

premier objet temporaire nommé temp est créé. alors le constructeur de copie copie le temp à l'endroit de la valeur de retour d'oustide. , puis le destructeur est appelé pour temp à la fin de la portée.

En fin de compte, la méthode 1 est plus efficace, mais c'est une fonctionnalité dépendant du compilateur.

+4

Chaque compilateur moderne non retardé émet exactement le même code pour les deux versions. En outre, vous oubliez la sémantique des déplacements. – Griwes

+0

mais dans la méthode 2, un objet temporaire y est créé y compris son appel de constructeur. –

+0

Peut-être qu'il me manque quelque chose, mais je ne vois pas pourquoi un optimiseur traiterait ces deux routines différemment les unes des autres. Ils retournent tous les deux un objet créé sur place qui n'est utilisé à aucune fin mais qui renvoie une valeur. –

0

La deuxième mise en œuvre entraînera l'optimisation des VNR. Stan Lippman a déclaré que l'optimisation NRV avait besoin d'un constructeur de copie explicite, mais ici la classe X est assez simple, donc je ne pense pas que NRV ait besoin d'un constructeur de copie explicite.

Questions connexes