Il y a quelques bonnes réponses déjà. Je vais me concentrer principalement sur ce que je pense qu'ils manquent de - une explication des "cons" avec la copie-et-swap idiome....
Qu'est-ce que la copie-et-swap idiome?
Mise en oeuvre de l'opérateur d'affectation en termes d'une fonction d'échange:
X& operator=(X rhs)
{
swap(rhs);
return *this;
}
L'idée fondamentale est que:
les plus sujettes à l'erreur le cadre de l'attribution d'un objet est d'assurer des ressources aux nouveaux besoins de l'état sont acquis (par exemple la mémoire, descripteurs)
cette acquisition peut être tentée avant la modification de l'état actuel de l'objet (c'est à dire *this
) si une copie de la nouvelle valeur, qui est pourquoi rhs
est acceptée par la valeur (c'est à dire copier) plutôt que par référence
la permutation de l'état de la copie locale rhs
et *this
est généralement relativement facile à faire sans échec potentiel/exceptions, compte tenu de la copie locale n'a pas besoin d'un état en particulier à la suite (faut juste adapter l'etat pour le destructeur à terme, comme pour un objet d'être déplacés de >= C++11)
Quand doit-il être utilisé? (Les problèmes qui permet-il de résoudre [/créer]?)
Lorsque vous souhaitez que le assignés à objecter pas affectée par une affectation qui lève une exception, en supposant que vous avez ou pouvez écrire un swap
, avec de fortes exception de la garantie, et idéalement un qui ne peut pas échouer/throw
..†
-
Lorsque vous voulez un endroit propre, facile à comprendre, robuste, de façon à définir l'opérateur d'affectation en termes de (plus simple) constructeur de copie, swap
et destructeur fonctions.
- L'auto-attribution de fait comme une copie-et-swap évite souvent négligé, des cas limites.‡
- Lors de toute exécution de peine ou momentanément plus élevé de l'utilisation des ressources créées par avoir un autre objet temporaire lors de l'affectation n'est pas important pour votre application. ⁂
† swap
lancer: il est généralement possible d'échanger des données de manière fiable les membres que les objets de la piste par pointeur, mais non le pointeur de données des membres qui n'ont pas un lancer de libre échange, ou pour lequel la permutation doit être mise en œuvre en tant que X tmp = lhs; lhs = rhs; rhs = tmp;
et la copie de la construction ou de la cession peut lever des, ont encore le potentiel de l'échec de la sortie de certains membres de données échangées et d'autres pas. Ce potentiel s'applique même à C++03 std::string
's que James commentaires sur une autre réponse:
@wilhelmtell: En C++03, il n'y a aucune mention des exceptions potentiellement jetés par des std::string::swap (qui est appelée par std::swap). Dans C++0x, std::string::swap est noexcept et ne doit pas jeter des exceptions. – James McNellis Déc 22 '10 à 15:24
‡ l'opérateur d'assignation de mise en œuvre qui semble sain d'esprit lors de l'affectation d'un objet distincts peut facilement échouer pour de l'auto-attribution. Bien qu'il puisse sembler inimaginable que le code client serait même tentative d'auto-attribution, il peut arriver assez facilement au cours algo opérations sur les conteneurs, avec x = f(x);
code f
est (peut-être seulement pour quelques - #ifdef
branches) une macro ala #define f(x) x
ou une fonction retournant une référence à l' x
, ou même (probablement inefficace mais concis) code comme celui - x = c1 ? x * 2 : c2 ? x / 2 : x;
). Par exemple:
struct X
{
T* p_;
size_t size_;
X& operator=(const X& rhs)
{
delete[] p_; // OUCH!
p_ = new T[size_ = rhs.size_];
std::copy(p_, rhs.p_, rhs.p_ + rhs.size_);
}
...
};
Sur l'auto-attribution, le code ci-dessus supprimer l' x.p_;
, les points de p_
à un nouvellement allouée région de tas, puis tente de lire le non initialisée données qu'ils contiennent (Comportement Indéfini), si ce n'est pas faire quelque chose de trop étrange, copy
des tentatives d'auto-attribution à chaque juste-destructed "T"!
⁂ La copie-et-swap idiome peut introduire de l'inefficacité ou des limitations en raison de l'utilisation d'un supplément temporaire (lorsque l'opérateur paramètre est exemplaire construit):
struct Client
{
IP_Address ip_address_;
int socket_;
X(const X& rhs)
: ip_address_(rhs.ip_address_), socket_(connect(rhs.ip_address_))
{ }
};
Ici, écrite à la main Client::operator=
peut vérifier si *this
est déjà connecté sur le même serveur que rhs
(peut-être l'envoi d'un "reset" code si utile), alors que la copie-et-swap approche serait d'appeler le constructeur par copie qui serait susceptible d'être écrite pour ouvrir une nette prise de connexion puis fermez l'original. Non seulement pourrait-il que cela signifie d'un réseau à distance de l'interaction au lieu d'une simple variable de processus de copie, il pourrait aller à l'encontre du client ou du serveur de limites sur les sockets ou les connexions. (Bien sûr, cette classe a une assez horrible interface, mais c'est une autre affaire ;-P).