Quelque chose me dérange:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
swap(tmp);
return *this;
}
Premièrement, lire le mot «swap» quand mon esprit pense «copier» irrite mon bon sens. Aussi, je remets en question le but de cette astuce sophistiquée. Oui, toutes les exceptions dans la construction des nouvelles ressources (copiées) devraient se produire avant l'échange, ce qui semble être un moyen sûr de s'assurer que toutes les nouvelles données sont remplies avant de les mettre en ligne.
C'est très bien. Alors, qu'en est-il des exceptions qui se produisent après l'échange? (lorsque les anciennes ressources sont détruites lorsque l'objet temporaire est hors de portée) Du point de vue de l'utilisateur de l'affectation, l'opération a échoué, sauf que ce n'est pas le cas. Cela a un énorme effet secondaire: la copie a effectivement eu lieu. Seul un nettoyage des ressources a échoué. L'état de l'objet de destination a été modifié même si l'opération semble avoir échoué de l'extérieur.
Donc, je propose au lieu de "swap" de faire un "transfert" plus naturel:
MyClass& operator=(const MyClass& other)
{
MyClass tmp(other);
transfer(tmp);
return *this;
}
Il y a toujours 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 déplacer (et de NULL pour qu'elles ne soient pas libérées deux fois) les ressources de la source vers elle.
Au lieu de {construire, déplacer, détruire}, je propose {construire, détruire, déplacer}. Le mouvement, qui est l'action la plus dangereuse, est celui qui est effectué en dernier après que tout le reste a été réglé.
Oui, l'échec de la destruction est un problème dans les deux schémas. Les données sont soit corrompues (copiées lorsque vous ne pensiez pas qu'elles l'étaient) ou perdues (libérées lorsque vous ne pensiez pas qu'elles l'étaient). Perdu vaut mieux que corrompu. Aucune donnée n'est meilleure que de mauvaises données.
Transférer au lieu de swap. C'est ma suggestion de toute façon.