97 votes

Constructeur de copie et l'opérateur = surcharge en C++: est une fonction commune possible?

Depuis un constructeur de copie

MyClass(const MyClass&);

et un opérateur = surcharge

MyClass& operator = (const MyClass&);

ont à peu près le même code, le même paramètre, et ne diffèrent que sur le retour, est-il possible d'avoir une fonction commune pour les deux?

135voto

Charles Bailey Points 244082

Oui. Il y a deux options communes. - Que je ne recommande pas - est l'appel de l' operator= du constructeur de copie explicitement:

MyClass(const MyClass& other)
{
    operator=(other);
}

Toutefois, en fournissant un bon operator= est un défi quand il s'agit de traiter avec l'ancien état et des questions découlant de l'auto attribution. Aussi, tous les membres et les bases obtenir initialisé par défaut en premier, même si elles sont cédées à partir d' other. Cela peut même ne pas être valable pour tous les membres et les bases, et même si il est valide, il est sémantiquement redondante et peut être pratiquement cher.

De plus en plus populaire, la solution est de mettre en oeuvre operator= en utilisant le constructeur de copie et une méthode d'échange.

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}

ou encore:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

Un swap fonction est généralement simple à écrire qu'il vient de swaps de la propriété de l'intérieur et ne pas avoir à nettoyer état existant ou d'allouer de nouvelles ressources.

Avantages de la copie et de swap idiome, c'est qu'il est automatiquement auto-attribution de sécurité et de fournir de l'échange de l'opération n'est pas remise est également fortement exception coffre-fort.

Pour être fortement exception coffre-fort, une "main" écrit opérateur d'affectation a généralement à attribuer une copie des nouvelles ressources avant de l'allocation du cessionnaire de la vieille ressources, de sorte que si une exception se produit l'allocation de nouvelles ressources, l'ancien état peut encore être retourné. Tout cela n'est gratuit à partir d'un copier-and-swap, mais est généralement plus complexe, et donc source d'erreurs, de le faire à partir de zéro.

La seule chose à faire attention est de s'assurer que la méthode d'échange est un véritable échange, et non pas la valeur par défaut std::swap qui utilise le constructeur de copie et l'opérateur d'affectation de lui-même.

Généralement, un memberwise swap est utilisé. std::swap fonctionne et est "pas de jeter" garanti avec tous les types de base et les types pointeur. La plupart des pointeurs intelligents peuvent également être échangé avec un pas de jet de garantie.

17voto

sbi Points 100828

Le constructeur de copie exécute le premier temps de l'initialisation des objets utilisés pour être cru de la mémoire. L'opérateur d'affectation, otoh, que, remplace les valeurs existantes par de nouvelles. Plus souvent que jamais, cela implique de rejeter la vieille ressources (par exemple, la mémoire) et de l'allocation de nouveaux.

Si il y a une similitude entre les deux, c'est que la cession de l'opérateur effectue la destruction et la copie de la construction. Certains développeurs ont utilisé pour la mise en œuvre de la cession en place de destruction suivie par le placement de copie de la construction. Cependant, c'est une très mauvaise idée. (Si c'est l'opérateur d'affectation d'une classe de base qui a appelé lors de l'affectation d'une classe dérivée?)

Ce qui est généralement considéré comme la forme canonique de l'idiome de nos jours est à l'aide de swap que Charles a proposé:

MyClass& operator=(MyClass other)
{
    swap(other);
    return *this;
}

Il utilise la copie de la construction (à noter qu' other est copié) et la destruction (il est détruit à la fin de la fonction) -- et il les utilise dans le bon ordre, trop: la construction (peut échouer) avant la destruction (qui ne doit pas échouer).

0voto

BostonLogan Points 1067

Oui. Vous avez peut-être privé de la fonction de Copie qui serait utilisé par les deux. Ou vous pouvez mettre en œuvre copier construcor en termes d'opérateur d'affectation. Il existe des alternatives un peu technique controversée, à mon avis. Vous pouvez le vérifier ici.

-3voto

Matthew Points 1

Quelque chose qui me dérange à propos de:

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    swap(tmp);
    return *this;
}

Tout d'abord, la lecture de la parole "swap" quand mon esprit est la pensée de la "copie" irrite mon bon sens. Aussi, je m'interroge sur le but de cette fantaisie truc. Oui, toutes les exceptions dans la construction de la nouvelle (copie) des ressources devrait se faire avant le swap, ce qui semble être un moyen sûr de s'assurer que toutes les nouvelles données est rempli avant de le faire vivre.

C'est très bien. Donc, concernant les exceptions qui se produisent après l'échange? (quand la vieille ressources sont détruits lorsque l'objet temporaire est hors de portée) du point De vue de l'utilisateur de la mission, l'opération a échoué, sauf qu'il n'a pas. Il a un énorme effet de bord: la copie ne se produisent réellement. C'était seulement un nettoyage des ressources qui a échoué. L'état de l'objet de destination a été modifié, même si l'opération semble de l'extérieur d'avoir échoué.

Donc, je propose plutôt de "swap" pour faire plus naturel "transfert":

MyClass& operator=(const MyClass& other)
{
    MyClass tmp(other);
    transfer(tmp);
    return *this;
}

Il y a encore de la construction de l'objet temporaire, mais la prochaine action immédiate est de libérer toutes les ressources actuelles de la destination, avant de se déplacer (et Invalide donc ils ne seront pas en double-libéré) les ressources de la source.

Au lieu de { construire, de se déplacer, détruire }, je propose { construire, détruire, déplacer }. Le mouvement, qui est le plus dangereux de l'action, qui est pris en dernier après que tout a été réglé.

Oui, la destruction échouer est un problème dans l'un des deux systèmes. Les données sont endommagées (copiées lorsque vous ne pensiez pas que c'était) ou de perte (libéré lorsque vous ne pensiez pas que c'était). Perdu, c'est mieux que corrompu. Aucune donnée n'est mieux que de mauvaises données.

Transfert au lieu de swap. C'est ma suggestion de toute façon.

Prograide.com

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.

Powered by:

X