浏览一些C ++问题我经常看到一些STL友好类应该实现swap
函数(通常作为朋友)的评论。有人可以解释一下它带来了什么好处,STL如何适应这个和为什么这个函数应该实现为friend
?
答案 0 :(得分:25)
对于大多数类,默认交换很好,但是,默认交换在所有情况下都不是最佳的。最常见的例子是使用Pointer to Implementation成语的类。与默认交换一样,大量内存会被复制,如果是专门的交换,只需交换指针就可以显着加快速度。
如果可能,它不应该是该类的朋友,但是它可能需要访问您可能不希望在类API中公开的私有数据(例如,原始指针)。
答案 1 :(得分:23)
标准版本的std :: swap()适用于大多数可分配的类型。
void std::swap(T& lhs,T& rhs)
{
T tmp(lhs);
lhs = rhs;
rhs = tmp;
}
但它不是一个最佳实现,因为它调用了复制构造函数,然后调用了两个赋值运算符。
通过为您的类添加自己的std :: swap()版本,您可以实现swap()的优化版本。
例如std :: vector。上面定义的默认实现非常昂贵,因为您需要复制整个数据区域。可能会释放旧数据区域或重新分配数据区域,并在复制的每个项目上调用包含类型的复制构造函数。一个专门的版本有一个非常简单的方法来做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);
}
因此,如果您的类可以优化swap()操作,那么您应该这样做。否则将使用默认版本。
我个人喜欢将swap()实现为非投掷成员方法。然后提供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);
}
答案 2 :(得分:17)
如果你想交换(例如)两个向量而不了解它们的实现,你基本上必须做这样的事情:
typedef std::vector<int> vec;
void myswap(vec &a, vec &b) {
vec tmp = a;
a = b;
b = tmp;
}
如果a
和b
包含许多元素,则效率不高,因为所有这些元素都是在a
,b
和tmp
之间复制的。
但是如果交换函数知道并且可以访问向量的内部,那么可能有更高效的实现:
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;
// ...
}
在这个实现中,只需要复制几个指针,而不是像第一个版本中那样复制所有元素。由于此实现需要访问向量的内部,因此它必须是友元函数。
答案 3 :(得分:10)
我将你的问题解释为基本上三个不同(相关)的问题。
swap
)?为什么STL需要交换?
STL友好类需要swap
的原因是swap
在许多STL算法中用作基本操作。 (例如reverse
,sort
,partition
等通常使用swap
实现
为什么要实施专门的交换(i.s.o.依赖于默认的
swap
)?
这部分问题已经有很多(好的)答案。基本上,了解类的内部经常允许您编写更加优化的swap
函数。
为什么要将它作为朋友实施?
STL算法总是将swap作为自由函数调用。所以它需要作为非成员函数可用才有用。
并且,因为当你可以使用内部结构的知识来编写更有效的交换时,编写自定义交换是唯一有益的,这意味着你的自由函数将需要访问你的类的内部,因此是朋友。
基本上,它 并不是朋友,但如果它不需要是朋友,那么通常也没有理由实现自定义交换。
请注意,您应确保free函数与您的类位于同一名称空间内,以便STL算法可以通过Koening查找找到您的自由函数。
答案 4 :(得分:9)
交换功能的另一个用途是帮助例外安全代码:http://www.gotw.ca/gotw/059.htm
答案 5 :(得分:2)
实现赋值运算符:
class C
{
C(C const&);
void swap(C&) throw();
C& operator=(C x) { this->swap(x); return *this; }
};
这是异常安全的,复制是在传递值时通过复制构造函数完成的,并且当您传递临时(通过copy elision)时编译器可以优化副本。
答案 6 :(得分:1)
效率:
如果你有一个拥有(智能)数据指针的类,那么交换指针可能比交换实际数据更快--3个指针拷贝与3个深拷贝。
如果你使用'using std :: swap'+对swap的非限定调用(或者只是对boost :: swap的限定调用),那么ADL将选择自定义交换函数,允许编写有效的模板代码。
安全性:
指针交换(原始指针,std :: auto_ptr和std :: tr1 :: shared_ptr)不抛出,因此可用于实现非抛出交换。非抛出交换可以更容易地编写提供强异常保证的代码(事务代码)。
一般模式是:
class MyClass
{
//other members etc...
void method()
{
MyClass finalState(*this);//copy the current class
finalState.f1();//a series of funcion calls that can modify the internal
finalState.f2();//state of finalState and/or throw.
finalState.f3();
//this only gets call if no exception is thrown - so either the entire function
//completes, or no change is made to the object's state at all.
swap(*this,finalState);
}
};
是否应该作为朋友实施;交换通常需要了解实现细节。无论是使用呼叫会员功能的非朋友还是使用朋友,都是一种品味问题。
问题:
自定义交换通常比单个分配更快 - 但是单个分配总是比默认的三个分配交换更快。如果你想移动一个对象,就不可能以通用的方式知道交换或赋值是否最好 - 这是C ++ 0x用移动构造函数解决的问题。