web-dev-qa-db-fra.com

Copie du constructeur et = surcharge de l'opérateur en C ++: une fonction commune est-elle possible?

Depuis un constructeur de copie

MyClass(const MyClass&);

et an = surcharge de l'opérateur

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 à utiliser par les deux?

80
MPelletier

Oui. Il existe deux options communes. L'une, généralement découragée, consiste à appeler le operator= du constructeur de copie explicitement:

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

Cependant, en fournissant un bon operator= est un défi lorsqu'il s'agit de faire face à l'ancien état et aux problèmes découlant de l'auto-affectation. De plus, tous les membres et bases sont initialisés par défaut en premier, même s'ils doivent être attribués à partir de other. Cela peut même ne pas être valable pour tous les membres et bases et même s'il est valide, il est sémantiquement redondant et peut être pratiquement coûteux.

Une solution de plus en plus populaire consiste à implémenter operator= en utilisant le constructeur de copie et une méthode de swap.

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

ou même:

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

Une fonction swap est généralement simple à écrire car elle échange simplement la propriété des éléments internes et n'a pas à nettoyer l'état existant ni à allouer de nouvelles ressources.

Les avantages de l'idiome de copie et d'échange sont qu'il est automatiquement sûr pour l'auto-affectation et - à condition que l'opération d'échange ne soit pas lancée - est également fortement protégé contre les exceptions.

Pour être fortement protégé contre les exceptions, un opérateur d'affectation écrit à la main doit généralement allouer une copie des nouvelles ressources avant de désallouer les anciennes ressources du cessionnaire de sorte que si une exception se produit lors de l'allocation des nouvelles ressources, l'ancien état peut toujours être retourné à . Tout cela est gratuit avec la copie et l'échange, mais est généralement plus complexe, et donc sujet aux erreurs, à faire à partir de zéro.

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

Généralement, un membre par membre swap est utilisé. std::swap fonctionne et est garanti "no-throw" avec tous les types de base et les types de pointeurs. La plupart des pointeurs intelligents peuvent également être échangés avec une garantie anti-lancer.

111
CB Bailey

Le constructeur de copie effectue la première initialisation d'objets qui étaient auparavant de la mémoire brute. L'opérateur d'affectation, OTOH, remplace les valeurs existantes par de nouvelles. Le plus souvent, cela implique de supprimer les anciennes ressources (par exemple, la mémoire) et d'en allouer de nouvelles.

S'il y a une similitude entre les deux, c'est que l'opérateur d'affectation effectue la destruction et la copie-construction. Certains développeurs avaient l'habitude d'implémenter l'affectation par destruction sur place suivie d'une construction de copie de placement. Cependant, c'est une très mauvaise idée. (Que faire s'il s'agit de 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 l'idiome canonique de nos jours utilise swap comme Charles l'a suggéré:

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

Cela utilise la construction de copie (notez que other est copié) et la destruction (elle est détruite à la fin de la fonction) - et elle les utilise également dans le bon ordre: construction (peut échouer) avant la destruction ( ne doit pas échouer).

12
sbi