1,最通用的模板交换函数模式:创建临时对象,调用对象的赋值操作符。
[cpp] view plain
copy
print ?
- template <class T> void swap ( T& a, T& b )
- {
- T c(a); a=b; b=c;
- }
需要构建临时对象,一个拷贝构造,两次赋值操作。
2,针对int型优化:
[cpp] view plain
copy
print ?
- void swap(int & __restrict a, int & __restrict b)
- {
- a ^= b;
- b ^= a;
- a ^= b;
- }
无需构造临时对象,异或
因为指针是int,所以基于这个思路可以优化1:
[cpp] view plain
copy
print ?
- template <typename T> void Swap(T & obj1,T & obj2)
- {
- unsigned char * pObj1 = reinterpret_castchar *>(&obj1);
- unsigned char * pObj2 = reinterpret_castchar *>(&obj2);
- for (unsigned long x = 0; x < sizeof(T); ++x)
- {
- pObj1[x] ^= pObj2[x];
- pObj2[x] ^= pObj1[x];
- pObj1[x] ^= pObj2[x];
- }
- }
3,针对内建类型的优化: int, flaot, double 等,甚至重载运算符的用户自定义类型:向量,矩阵,图像等。。。
type a; -- e.g 10
type b; -- e.g 5
a = a+b ; -- a=15,b=5
b = a-b ; -- a=15,b=10
a= a -b ; -- a= 5,b=10
// 无需构造临时变量。使用基本运算操作符。
[cpp] view plain
copy
print ?
- Ok, let's see.
- a = a + b;
- b = a - b;
- a = a - b;
- Let's introduce new names
- c = a + b;
- d = c - b;
- e = c - d;
- And we want to prove that d == a and e == b.
- d = (a + b) - b = a, proved.
- e = (a + b) - ((a + b) - b) = (a + b) - a = b, proved.
- For all real numbers.
4,swap的一些特化:
std::string, std::vector各自实现了swap函数,
string中
[cpp] view plain
copy
print ?
- template<class _Elem,
- class _Traits,
- class _Alloc> inline
- void __CLRCALL_OR_CDECL swap(basic_string<_Elem, _Traits, _Alloc>& _Left,
- basic_string<_Elem, _Traits, _Alloc>& _Right)
- {
- _Left.swap(_Right);
- }
- void __CLR_OR_THIS_CALL swap(_Myt& _Right)
- {
- if (this == &_Right)
- ;
- else if (_Mybase::_Alval == _Right._Alval)
- {
- #if _HAS_ITERATOR_DEBUGGING
- this->_Swap_all(_Right);
- #endif /* _HAS_ITERATOR_DEBUGGING */
- _Bxty _Tbx = _Bx;
- _Bx = _Right._Bx, _Right._Bx = _Tbx;
- size_type _Tlen = _Mysize;
- _Mysize = _Right._Mysize, _Right._Mysize = _Tlen;
- size_type _Tres = _Myres;
- _Myres = _Right._Myres, _Right._Myres = _Tres;
- }
- else
- {
- _Myt _Tmp = *this;
- *this = _Right;
- _Right = _Tmp;
- }
- }
第二个swap(Right)进行判断,如果使用了相同的分配器,则直接交换控制信息,否则调用string::operator=进行拷贝赋值。。。所以建议优先使用swap函数,而不是赋值操作符。
vector中
[cpp] view plain
copy
print ?
- template<class _Ty,
- class _Alloc> inline
- void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right)
- {
- _Left.swap(_Right);
- }
- void swap(_Myt& _Right)
- {
- if (this == &_Right)
- ;
- else if (this->_Alval == _Right._Alval)
- {
- #if _HAS_ITERATOR_DEBUGGING
- this->_Swap_all(_Right);
- #endif /* _HAS_ITERATOR_DEBUGGING */
- this->_Swap_aux(_Right);
- _STD swap(_Myfirst, _Right._Myfirst);
- _STD swap(_Mylast, _Right._Mylast);
- _STD swap(_Myend, _Right._Myend);
- }
- else
- {
- this->_Swap_aux(_Right);
- _Myt _Ts = *this;
- *this = _Right;
- _Right = _Ts;
- }
- }
vector的swap原理跟string完全一致,只有当当使用了不同分配器才进行字节拷贝。其余情况直接交换控制信息。
测试用例:
5,Copy and Swap idiom
目的:C++异常有三个级别:基本,强,没有异常。通过创建临时对象然后交换,能够实现重载赋值操作符的强异常安全的执行。
Loki中智能指针 临时变量跟this交换,临时变量自动销毁~
[cpp] view plain
copy
print ?
- SmartPtr& operator=(SmartPtr& rhs)
- {
- SmartPtr temp(rhs);
- temp.Swap(*this);
- return *this;
- }
boost::share_ptr,share_ptr定义了自己的swap函数。
[cpp] view plain
copy
print ?
- shared_ptr & operator=( shared_ptr const & r )
- {
- this_type(r).swap(*this);
- return *this;
- }
- void swap(shared_ptr & other)
- {
- std::swap(px, other.px);
- pn.swap(other.pn);
- }
记得本科上C++课,老师特别喜欢拿String来举例子,面试题也特别喜欢String。。。下面说说String::opreator=函数的优化:
最一般的写法,特点:使用const string& 传参防止临时对象。
[cpp] view plain
copy
print ?
- String& String::operator =(const String & rhs)
- {
- if (itsString)
- delete [] itsString;
- itsLen = rhs.GetLen();
- itsString = new char[itsLen+1];
- for (unsigned short i = 0;i
- itsString[i] = rhs[i];
- itsString[itsLen] = '/0';
- return *this;
- }
优化1,防止自我间接赋值,a = b; c = b; a = c; 如果没有第一个if判断,当把c赋给a的时候,删除了a.itsString,后面的拷贝就会出错。注意是if(this==&rhs), 而不是if(*this==rhs).
[cpp] view plain
copy
print ?
- String& String::operator =(const String & rhs)
- {
- if (this == &rhs)
- return *this;
- if (itsString)
- delete [] itsString;
- itsLen=rhs.GetLen();
- itsString = new char[itsLen+1];
- for (unsigned short i = 0;i
- itsString[i] = rhs[i];
- itsString[itsLen] = '/0';
- return *this;
- }
优化2,不进行拷贝赋值,只是交换控制信息,而且是强异常安全:
[cpp] view plain
copy
print ?
- String & String::operator = (String const &rhs)
- {
- if (this != &rhs)
- String(rhs).swap (*this);
-
- return *this;
- }
优化3,以最原始的传值方式传参,避免临时对象创建:
[cpp] view plain
copy
print ?
- String & operator = (String s)
- {
- s.swap (*this);
- return *this;
- }
最后这张方式主要是对C++新特性rvalue的优化,具体参见:http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap
6. vector clear and swap trick
vector.clear并只是将size变量置为0,并没有及时归还OS,STL仍然持有内存,以便后续push_back。实测如下:
[cpp] view plain
copy
print ?
- vector<int> temp;
此时打开资源管理器,内存如下:
增长vector然后清空:
[cpp] view plain
copy
print ?
- temp.resize( 1024*1024*20 );
- temp.clear();
此时资源管理器内存:
clear以后进程兵没有及时将内存归还OS。。。通过swap方法:
[cpp] view plain
copy
print ?
- tmp.resize(1024*1024*20);
-
- {
- std::vector<int>().swap(tmp);
- }
退出作用域,临时对象销毁。内存归还OS。此时资源管理器中进程内存回到1,864K。
附上网络版的String:
参考引用:
1,http://www.vbforums.com/showthread.php?t=245517
2,http://www.cplusplus.com/reference/algorithm/swap/
3,http://codeguru.earthweb.com/forum/showthread.php?t=485643
4,http://stackoverflow.com/questions/1998744/benefits-of-a-swap-function
5,http://answers.google.com/answers/threadview/id/251027.html
C++ idioms
http://en.wikibooks.org/wiki/Category:More_C%2B%2B_Idioms
Copy and Swap idiom
http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
History:
20140401 - add 6 vector clear and swap trick!