En parcourant certaines questions C ++, j'ai souvent vu des commentaires selon lesquels une classe compatible STL devrait implémenter une fonction swap
(généralement en tant qu'ami). Quelqu'un peut-il expliquer quels avantages cela apporte, comment la STL s'intègre à cela et pourquoi cette fonction doit être implémentée en friend
?
Réponses
Trop de publicités?Pour la plupart des classes, la valeur par défaut swap est très bien, cependant, le défaut de swap n'est pas optimal dans tous les cas. L'exemple le plus commun de ce qui serait une classe à l'aide du Pointeur de la mise en Œuvre de l'idiome. Si le contrat d'échange sur défaut d'une grande quantité de mémoire serait d'avoir copié, c'est que vous spécialisé swap, vous pouvez l'accélérer de façon significative par la seule permutation des pointeurs.
Si possible, il ne devrait pas être un ami de la classe, mais il peut avoir besoin d'accéder à des données privées (par exemple, la crue des pointeurs) qui vous classe n'a probablement pas envie d'exposer dans la classe de l'API.
La version standard de std::swap() fonctionnera pour la plupart des types qui sont cessibles.
void std::swap(T& lhs,T& rhs)
{
T tmp(lhs);
lhs = rhs;
rhs = tmp;
}
Mais ce n'est pas une mise en œuvre optimale, car il fait un appel au constructeur de copie suivie par les deux appels à l'opérateur d'affectation.
En ajoutant votre propre version de std::swap() de votre classe, vous pouvez mettre en œuvre une version optimisée de swap().
Par exemple std::vector. L'implémentation par défaut tel que défini ci-dessus serait très coûteux que vous auriez besoin de faire une copie de l'ensemble de la zone de données. Potentiellement la libération des données anciennes zones ou de ré-allouer de la zone de données ainsi que d'invoquer le constructeur de copie pour les contenus de type sur chaque élément copié. Une version spéciale est très simple moyen facile de le faire std::swap()
// NOTE this is not real code.
// It is just an example to show how much more effecient swaping a vector could
// be. And how using a temporary for the vector object is not required.
std::swap(std::vector<T>& lhs,std::vector<T>& rhs)
{
std::swap(lhs.data,rhs.data); // swap a pointer to the data area
std::swap(lhs.size,rhs.size); // swap a couple of integers with size info.
std::swap(lhs.resv,rhs.resv);
}
En conséquence, si votre classe peut optimiser le swap() de l'opération, alors vous devriez probablement faire. Sinon, la version par défaut sera utilisé.
Personnellement, j'aime à mettre en œuvre swap() non jetant un membre de méthode. Ensuite fournir une version spécialisée de std::swap():
class X
{
public:
// As a side Note:
// This is also useful for any non trivial class
// Allows the implementation of the assignment operator
// using the copy swap idiom.
void swap(X& rhs) throw (); // No throw exception guarantee
};
// Should be in the same namespace as X.
// This will allows ADL to find the correct swap when used by objects/functions in
// other namespaces.
void swap(X& lhs,X& rhs)
{
lhs.swap(rhs);
}
Si vous voulez échanger (par exemple) deux vecteurs sans rien connaître de leur mise en œuvre, en gros, vous avez à faire quelque chose comme ceci:
typedef std::vector<int> vec;
void myswap(vec &a, vec &b) {
vec tmp = a;
a = b;
b = tmp;
}
Ce n'est pas efficace si a
et b
contenir de nombreux éléments, puisque tous ces éléments sont copiés entre a
, b
et tmp
.
Mais si la fonction d'échange permettrait de connaître et d'avoir accès à l'intérieur du vecteur, il pourrait y avoir une application plus efficace possible:
void std::swap(vec &a, vec &b) {
// assuming the elements of the vector are actually stored in some memory area
// pointed to by vec::data
void *tmp = a.data;
a.data = b.data;
b.data = tmp;
// ...
}
Dans cette mise à seulement quelques pointeurs doivent être copiés, pas tous les éléments comme dans la première version. Et depuis cette mise en œuvre a besoin d'accéder à l'intérieur du vecteur, il doit être un ami de la fonction.
J'ai interprété votre question essentiellement trois différentes (liées) questions.
- Pourquoi ne STL besoin de swap?
- Pourquoi devrait spécialisé swap être mis en place (j'.s.o. en s'appuyant sur la valeur par défaut
swap
)? - Pourquoi devrait-il être mis en œuvre comme un ami?
Pourquoi ne STL besoin de swap?
La raison STL amical les besoins de la classe swap
que swap
est utilisé comme un primitif dans de nombreux algorithmes de la STL. (par exemple, reverse
, sort
, partition
etc. sont généralement mis en œuvre à l'aide de swap
)
Pourquoi devrait spécialisé swap être mis en place (j'.s.o. en s'appuyant sur la valeur par défaut
swap
)?
Il y a beaucoup de (bonnes) réponses à cette partie de votre question déjà. Fondamentalement, connaître le fonctionnement interne d'une classe fréquemment vous permet d'écrire un beaucoup plus optimisé swap
fonction.
Pourquoi devrait-il être mis en œuvre comme un ami?
Les algorithmes de la STL sera toujours appeler swap comme une fonction libre. Il doit donc être disponible en fonction de membre pour être utile.
Et, puisque c'est seulement bénéfique pour écrire une personnalisation de l'échange lorsque vous pouvez utiliser la connaissance des structures internes pour écrire un beaucoup plus efficace de swap, cela signifie que votre fonction libre aura besoin d'accéder à l'intérieur de votre classe, donc un ami.
Fondamentalement, il ne veut pas avoir à être un ami, mais si elle n'a pas besoin d'être un ami, il n'y a généralement pas de raison de mettre en œuvre une coutume swap soit.
Notez que vous devez vous assurer que la fonction est à l'intérieur du même espace de noms que votre classe, afin que les algorithmes de la STL pouvez trouver votre fonction libre via Koening de recherche.
Une autre utilisation de la fonction d'échange est de faciliter le code d'exception: http://www.gotw.ca/gotw/059.htm