首页 > 代码库 > 【转】 谈谈C++中的swap函数
【转】 谈谈C++中的swap函数
1,最通用的模板交换函数模式:创建临时对象,调用对象的赋值操作符。
1 template <class T> void swap ( T& a, T& b ) 2 { 3 T c(a); a=b; b=c; 4 } 5
需要构建临时对象,一个拷贝构造,两次赋值操作。
2,针对int型优化:
1 void swap(int & __restrict a, int & __restrict b) 2 { 3 a ^= b; 4 b ^= a; 5 a ^= b; 6 }
无需构造临时对象,异或
因为指针是int,所以基于这个思路可以优化1:
1 template <typename T> void Swap(T & obj1,T & obj2) 2 { 3 unsigned char * pObj1 = reinterpret_cast<unsigned char *>(&obj1); 4 unsigned char * pObj2 = reinterpret_cast<unsigned char *>(&obj2); 5 for (unsigned long x = 0; x < sizeof(T); ++x) 6 { 7 pObj1[x] ^= pObj2[x]; 8 pObj2[x] ^= pObj1[x]; 9 pObj1[x] ^= pObj2[x]; 10 } 11 }
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
// 无需构造临时变量。使用基本运算操作符。
1 Ok, let‘s see. 2 a = a + b; 3 b = a - b; 4 a = a - b; 5 Let‘s introduce new names 6 c = a + b; 7 d = c - b; 8 e = c - d; 9 And we want to prove that d == a and e == b. 10 d = (a + b) - b = a, proved. 11 e = (a + b) - ((a + b) - b) = (a + b) - a = b, proved. 12 For all real numbers.
4,swap的一些特化:
std::string, std::vector各自实现了swap函数,
string中
1 template<class _Elem, 2 class _Traits, 3 class _Alloc> inline 4 void __CLRCALL_OR_CDECL swap(basic_string<_Elem, _Traits, _Alloc>& _Left, 5 basic_string<_Elem, _Traits, _Alloc>& _Right) 6 { // swap _Left and _Right strings 7 _Left.swap(_Right); 8 } 9 void __CLR_OR_THIS_CALL swap(_Myt& _Right) 10 { // exchange contents with _Right 11 if (this == &_Right) 12 ; // same object, do nothing 13 else if (_Mybase::_Alval == _Right._Alval) 14 { // same allocator, swap control information 15 #if _HAS_ITERATOR_DEBUGGING 16 this->_Swap_all(_Right); 17 #endif /* _HAS_ITERATOR_DEBUGGING */ 18 _Bxty _Tbx = _Bx; 19 _Bx = _Right._Bx, _Right._Bx = _Tbx; 20 size_type _Tlen = _Mysize; 21 _Mysize = _Right._Mysize, _Right._Mysize = _Tlen; 22 size_type _Tres = _Myres; 23 _Myres = _Right._Myres, _Right._Myres = _Tres; 24 } 25 else 26 { // different allocator, do multiple assigns 27 _Myt _Tmp = *this; 28 *this = _Right; 29 _Right = _Tmp; 30 } 31 }
第二个swap(Right)进行判断,如果使用了相同的分配器,则直接交换控制信息,否则调用string: perator=进行拷贝赋值。。。所以建议优先使用swap函数,而不是赋值操作符。
vector中
1 template<class _Ty, 2 class _Alloc> inline 3 void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) 4 { // swap _Left and _Right vectors 5 _Left.swap(_Right); 6 } 7 void swap(_Myt& _Right) 8 { // exchange contents with _Right 9 if (this == &_Right) 10 ; // same object, do nothing 11 else if (this->_Alval == _Right._Alval) 12 { // same allocator, swap control information 13 #if _HAS_ITERATOR_DEBUGGING 14 this->_Swap_all(_Right); 15 #endif /* _HAS_ITERATOR_DEBUGGING */ 16 this->_Swap_aux(_Right); 17 _STD swap(_Myfirst, _Right._Myfirst); 18 _STD swap(_Mylast, _Right._Mylast); 19 _STD swap(_Myend, _Right._Myend); 20 } 21 else 22 { // different allocator, do multiple assigns 23 this->_Swap_aux(_Right); 24 _Myt _Ts = *this; 25 *this = _Right; 26 _Right = _Ts; 27 } 28 }
vector的swap原理跟string完全一致,只有当当使用了不同分配器才进行字节拷贝。其余情况直接交换控制信息。
测试用例:
5,Copy and Swap idiom
目的:C++异常有三个级别:基本,强,没有异常。通过创建临时对象然后交换,能够实现重载赋值操作符的强异常安全的执行。
Loki中智能指针 临时变量跟this交换,临时变量自动销毁~
1 SmartPtr& operator=(SmartPtr<T1, OP1, CP1, KP1, SP1, CNP1 >& rhs) 2 { 3 SmartPtr temp(rhs); 4 temp.Swap(*this); 5 return *this; 6 }
boost::share_ptr,share_ptr定义了自己的swap函数。
1 shared_ptr & operator=( shared_ptr const & r ) // never throws 2 { 3 this_type(r).swap(*this); 4 return *this; 5 } 6 void swap(shared_ptr<T> & other) // never throws 7 { 8 std::swap(px, other.px); 9 pn.swap(other.pn); 10 } 11
记得本科上C++课,老师特别喜欢拿String来举例子,面试题也特别喜欢String。。。下面说说String: preator=函数的优化:
最一般的写法,特点:使用const string& 传参防止临时对象。
1 String& String: :o perator =(const String & rhs) 2 { 3 if (itsString) 4 delete [] itsString; 5 itsLen = rhs.GetLen(); 6 itsString = new char[itsLen+1]; 7 for (unsigned short i = 0;i<itsLen;i++) 8 itsString[i] = rhs[i]; 9 itsString[itsLen] = ‘/0‘; 10 return *this; 11 }
优化1,防止自我间接赋值,a = b; c = b; a = c; 如果没有第一个if判断,当把c赋给a的时候,删除了a.itsString,后面的拷贝就会出错。注意是if(this==&rhs), 而不是if(*this==rhs) .
1 String& String: :o perator =(const String & rhs) 2 { 3 if (this == &rhs) 4 return *this; 5 if (itsString) 6 delete [] itsString; 7 itsLen=rhs.GetLen(); 8 itsString = new char[itsLen+1]; 9 for (unsigned short i = 0;i<itsLen;i++) 10 itsString[i] = rhs[i]; 11 itsString[itsLen] = ‘/0‘; 12 return *this; 13 }
优化2,不进行拷贝赋值,只是交换控制信息,而且是强异常安全:
1 String & String: :o perator = (String const &rhs) 2 { 3 if (this != &rhs) 4 String(rhs).swap (*this); // Copy-constructor and non-throwing swap 5 // Old resources are released with the destruction of the temporary above 6 return *this; 7 }
优化3,以最原始的传值方式传参,避免临时对象创建:
1 String & operator = (String s) // the pass-by-value parameter serves as a temporary 2 { 3 s.swap (*this); // Non-throwing swap 4 return *this; 5 }// Old resources released when destructor of s is called. 6
最后这张方式主要是对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。实测如下:
1 vector<int> temp;
此时打开资源管理器,内存如下:
增长vector然后清空:
1 temp.resize( 1024*1024*20 ); // 80M 2 temp.clear();
此时资源管理器内存:
clear以后进程兵没有及时将内存归还OS。。。通过swap方法:
1 tmp.resize(1024*1024*20); // 80M 2 // tmp.clear(); 3 { 4 std::vector<int>().swap(tmp); // 将内存归还OS 5 }
退出作用域,临时对象销毁。内存归还OS。此时资源管理器中进程内存回到1,864K。
附上网络版的String:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 class String 5 { 6 public: 7 String(); 8 String(const char *const); 9 String(const String & ;) ; 10 ~String(); 11 char & operator[] (unsigned short offset); 12 char operator[] (unsigned short offset)const; 13 String operator+(const String& ;) ; 14 void operator+=(const String& ;) ; 15 String & operator= (const String & ;) ; 16 unsigned short GetLen()const {return itsLen;} 17 const char * GetString()const {return itsString;} 18 private: 19 String (unsigned short); 20 char * itsString; 21 unsigned short itsLen; 22 }; 23 String::String() 24 { 25 itsString = new char[1]; //为什么设置成1,这样会导致内存1bytes无法释放吗?我觉得和itsString = new char没区别,那他为什么要设置成1,这样有什么用?21天学会C++那本书,我也有 ,书上也确实是设置成1. 26 itsString[0] = ‘/0‘; 27 itsLen=0; 28 } 29 String::String(unsigned short len) 30 { 31 itsString = new char[len+1]; 32 for (unsigned short i =0;i<=len;i++) 33 itsString[i] = ‘/0‘; 34 itsLen=len; 35 } 36 String::String(const char * const cString) 37 { 38 itsLen = strlen(cString); 39 itsString = new char[itsLen+1]; 40 for (unsigned short i=0;i<itsLen;i++) 41 itsString[i] = cString[i]; 42 itsString[itsLen] = ‘/0‘; 43 } 44 String::String(const String & rhs) 45 { 46 itsLen = rhs.GetLen(); 47 itsString = new char[itsLen+1]; 48 for (unsigned short i = 0;i<itsLen;i++) 49 itsString[i] = rhs[i]; 50 itsString[itsLen] = ‘/0‘; 51 } 52 String::~String() 53 { 54 delete [] itsString; 55 itsLen = 0; 56 } 57 String& String: :o perator =(const String & rhs) 58 { 59 if (this == &rhs) 60 return *this; 61 delete [] itsString; 62 itsLen=rhs.GetLen(); 63 itsString = new char[itsLen+1]; 64 for (unsigned short i = 0;i<itsLen;i++) 65 itsString[i] = rhs[i]; 66 itsString[itsLen] = ‘/0‘; 67 return *this; 68 } 69 char & String: :o perator [](unsigned short offset) //这个程序这样写,起到了什么用处??和main中的那一个对应? 70 { 71 if (offset > itsLen) 72 return itsString[itsLen-1]; //这个返回itslen-1到底是什么意思?为什么要减去1 ?? 73 else 74 return itsString[offset]; 75 } 76 char String: :o perator [](unsigned short offset)const 77 { 78 if (offset > itsLen) 79 itsString[itsLen-1]; 80 else 81 return itsString[offset]; 82 } 83 String String: :o perator +(const String& rhs) 84 { 85 unsigned short totalLen = itsLen + rhs.GetLen(); 86 String temp(totalLen); 87 unsigned short i; 88 for (i=0;i<itsLen;i++) 89 temp[i] = itsString[i]; 90 for (unsigned short j = 0;j<rhs.GetLen();j++,i++) 91 temp[i] = rhs[j]; 92 temp[totalLen] = ‘/0‘; 93 return temp; 94 } 95 void String: :o perator +=(const String& rhs) 96 { 97 unsigned short rhsLen = rhs.GetLen(); 98 unsigned short totalLen = itsLen + rhsLen; 99 String temp(totalLen); 100 unsigned short i; 101 for (i = 0;i<itsLen;i++) 102 temp[i] = itsString[i]; 103 for (unsigned short j = 0;j<rhs.GetLen();j++,i++) 104 temp[i] = rhs[i-itsLen]; 105 temp[totalLen] = ‘/0‘; 106 } 107 int main() 108 { 109 String s1("initial test"); //调用了什么函数? 110 cout<<"S1:/t"<<s1.GetString()<<endl; 111 char *temp ="Hello World"; 112 s1 = temp;//调用了什么函数? 113 cout<<"S1:/t"<<s1.GetString()<<endl; 114 char tempTwo[20]; 115 strcpy(tempTwo,"; nice to be here!"); 116 s1 += tempTwo; 117 cout<<"tempTwo:/t"<<tempTwo<<endl; 118 cout<<"S1:/t"<<s1.GetString()<<endl; 119 cout<<"S1[4]:/t"<<s1[4]<<endl; 120 cout<<"S1[999]:/t"<<s1[999]<<endl;//调用了什么函数? 121 String s2(" Anoter string");//调用了什么函数? 122 String s3; 123 s3 = s1+s2; 124 cout<<"S3:/t" <<s3.GetString()<<endl; 125 String s4; 126 s4 = "Why does this work?";//调用了什么函数? 127 cout<<"S4:/t"<<s4.GetString()<<endl; 128 return 0; 129 }
参考引用:
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!
原文:http://blog.csdn.net/ryfdizuo/article/details/6435847
【转】 谈谈C++中的swap函数