[置顶] 谈谈C++中的swap函数

1,最通用的模板交换函数模式:创建临时对象,调用对象的赋值操作符

template <class T> void swap ( T& a, T& b ) { T c(a); a=b; b=c; } 

需要构建临时对象,一个拷贝构造,两次赋值操作。

2,针对int型优化:

void swap(int & __restrict a, int & __restrict b) { a ^= b; b ^= a; a ^= b; }  

无需构造临时对象,异或

因为指针是int,所以基于这个思路可以优化1:

 

template <typename T> void Swap(T & obj1,T & obj2) { unsigned char * pObj1 = reinterpret_cast<unsigned char *>(&obj1); unsigned char * pObj2 = reinterpret_cast<unsigned char *>(&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

// 无需构造临时变量。使用基本运算操作符。

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

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) { // swap _Left and _Right strings _Left.swap(_Right); } void __CLR_OR_THIS_CALL swap(_Myt& _Right) { // exchange contents with _Right if (this == &_Right) ; // same object, do nothing else if (_Mybase::_Alval == _Right._Alval) { // same allocator, swap control information #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 { // different allocator, do multiple assigns _Myt _Tmp = *this; *this = _Right; _Right = _Tmp; } } 

第二个swap(Right)进行判断,如果使用了相同的分配器,则直接交换控制信息,否则调用string::operator=进行拷贝赋值。。。所以建议优先使用swap函数,而不是赋值操作符。

 

vector

template<class _Ty, class _Alloc> inline void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) { // swap _Left and _Right vectors _Left.swap(_Right); } void swap(_Myt& _Right) { // exchange contents with _Right if (this == &_Right) ; // same object, do nothing else if (this->_Alval == _Right._Alval) { // same allocator, swap control information #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 { // different allocator, do multiple assigns this->_Swap_aux(_Right); _Myt _Ts = *this; *this = _Right; _Right = _Ts; } }

vector的swap原理跟string完全一致,只有当当使用了不同分配器才进行字节拷贝。其余情况直接交换控制信息。

 

测试用例:

 

5,Copy and  Swap idiom

目的:C++异常有三个级别:基本没有异常。通过创建临时对象然后交换,能够实现重载赋值操作符的强异常安全的执行。

Loki中智能指针 临时变量跟this交换,临时变量自动销毁~

SmartPtr& operator=(SmartPtr<T1, OP1, CP1, KP1, SP1, CNP1 >& rhs) { SmartPtr temp(rhs); temp.Swap(*this); return *this; } 

boost::share_ptr,share_ptr定义了自己的swap函数。

shared_ptr & operator=( shared_ptr const & r ) // never throws { this_type(r).swap(*this); return *this; } void swap(shared_ptr<T> & other) // never throws { std::swap(px, other.px); pn.swap(other.pn); } 

记得本科上C++课,老师特别喜欢拿String来举例子,面试题也特别喜欢String。。。下面说说String::opreator=函数的优化:

最一般的写法,特点:使用const string& 传参防止临时对象。

String& String::operator =(const String & rhs) { if (itsString) delete [] itsString; itsLen = rhs.GetLen(); itsString = new char[itsLen+1]; for (unsigned short i = 0;i<itsLen;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) .

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<itsLen;i++) itsString[i] = rhs[i]; itsString[itsLen] = '/0'; return *this; } 

优化2,不进行拷贝赋值,只是交换控制信息,而且是强异常安全:

String & String::operator = (String const &rhs) { if (this != &rhs) String(rhs).swap (*this); // Copy-constructor and non-throwing swap // Old resources are released with the destruction of the temporary above return *this; } 

优化3,以最原始的传值方式传参,避免临时对象创建:

String & operator = (String s) // the pass-by-value parameter serves as a temporary { s.swap (*this); // Non-throwing swap return *this; }// Old resources released when destructor of s is called. 

最后这张方式主要是对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。实测如下

vector<int> temp;
此时打开资源管理器,内存如下:


增长vector然后清空:

temp.resize( 1024*1024*20 );    // 80M
temp.clear();
此时资源管理器内存:

clear以后进程兵没有及时将内存归还OS。。。通过swap方法:

 tmp.resize(1024*1024*20);	 // 80M
 // tmp.clear();
 {
      std::vector<int>().swap(tmp);        // 将内存归还OS
 }
退出作用域,临时对象销毁。内存归还OS。此时资源管理器中进程内存回到1,864K。


附上网络版的String:

#include <iostream> #include <cstring> using namespace std; class String { public: String(); String(const char *const); String(const String &); ~String(); char & operator[] (unsigned short offset); char operator[] (unsigned short offset)const; String operator+(const String&); void operator+=(const String&); String & operator= (const String &); unsigned short GetLen()const {return itsLen;} const char * GetString()const {return itsString;} private: String (unsigned short); char * itsString; unsigned short itsLen; }; String::String() { itsString = new char[1]; //为什么设置成1,这样会导致内存1bytes无法释放吗?我觉得和itsString = new char没区别,那他为什么要设置成1,这样有什么用?21天学会C++那本书,我也有 ,书上也确实是设置成1. itsString[0] = '/0'; itsLen=0; } String::String(unsigned short len) { itsString = new char[len+1]; for (unsigned short i =0;i<=len;i++) itsString[i] = '/0'; itsLen=len; } String::String(const char * const cString) { itsLen = strlen(cString); itsString = new char[itsLen+1]; for (unsigned short i=0;i<itsLen;i++) itsString[i] = cString[i]; itsString[itsLen] = '/0'; } String::String(const String & rhs) { itsLen = rhs.GetLen(); itsString = new char[itsLen+1]; for (unsigned short i = 0;i<itsLen;i++) itsString[i] = rhs[i]; itsString[itsLen] = '/0'; } String::~String() { delete [] itsString; itsLen = 0; } String& String::operator =(const String & rhs) { if (this == &rhs) return *this; delete [] itsString; itsLen=rhs.GetLen(); itsString = new char[itsLen+1]; for (unsigned short i = 0;i<itsLen;i++) itsString[i] = rhs[i]; itsString[itsLen] = '/0'; return *this; } char & String::operator [](unsigned short offset) //这个程序这样写,起到了什么用处??和main中的那一个对应? { if (offset > itsLen) return itsString[itsLen-1]; //这个返回itslen-1到底是什么意思?为什么要减去1 ?? else return itsString[offset]; } char String::operator [](unsigned short offset)const { if (offset > itsLen) itsString[itsLen-1]; else return itsString[offset]; } String String::operator +(const String& rhs) { unsigned short totalLen = itsLen + rhs.GetLen(); String temp(totalLen); unsigned short i; for (i=0;i<itsLen;i++) temp[i] = itsString[i]; for (unsigned short j = 0;j<rhs.GetLen();j++,i++) temp[i] = rhs[j]; temp[totalLen] = '/0'; return temp; } void String::operator +=(const String& rhs) { unsigned short rhsLen = rhs.GetLen(); unsigned short totalLen = itsLen + rhsLen; String temp(totalLen); unsigned short i; for (i = 0;i<itsLen;i++) temp[i] = itsString[i]; for (unsigned short j = 0;j<rhs.GetLen();j++,i++) temp[i] = rhs[i-itsLen]; temp[totalLen] = '/0'; } int main() { String s1("initial test"); //调用了什么函数? cout<<"S1:/t"<<s1.GetString()<<endl; char *temp ="Hello World"; s1 = temp;//调用了什么函数? cout<<"S1:/t"<<s1.GetString()<<endl; char tempTwo[20]; strcpy(tempTwo,"; nice to be here!"); s1 += tempTwo; cout<<"tempTwo:/t"<<tempTwo<<endl; cout<<"S1:/t"<<s1.GetString()<<endl; cout<<"S1[4]:/t"<<s1[4]<<endl; cout<<"S1[999]:/t"<<s1[999]<<endl;//调用了什么函数? String s2(" Anoter string");//调用了什么函数? String s3; s3 = s1+s2; cout<<"S3:/t" <<s3.GetString()<<endl; String s4; s4 = "Why does this work?";//调用了什么函数? cout<<"S4:/t"<<s4.GetString()<<endl; return 0; } 

 

参考引用:

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!


你可能感兴趣的:(C++,vector,String,iterator,debugging)