談談C++中的swap函式
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_cast<unsigned char *>(&obj1);
- unsigned char * pObj2 = reinterpret_cast<unsigned
- 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)
- { // 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
- elseif (_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中
[cpp] view plain copy print?- 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
- elseif (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交換,臨時變數自動銷燬~
[cpp] view plain copy print?- SmartPtr& operator=(SmartPtr<T1, OP1, CP1, KP1, SP1, CNP1 >& 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 ) // 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& 傳參防止臨時物件。
[cpp] view plain copy print?- String& String::operator =(const String & rhs)
- {
- if (itsString)
- delete [] itsString;
- itsLen = rhs.GetLen();
- itsString = newchar[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).
[cpp] view plain copy print?- String& String::operator =(const String & rhs)
- {
- if (this == &rhs)
- return *this;
- if (itsString)
- delete [] itsString;
- itsLen=rhs.GetLen();
- itsString = newchar[itsLen+1];
- for (unsigned short i = 0;i<itsLen;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); // Copy-constructor and non-throwing swap
- // Old resources are released with the destruction of the temporary above
- return *this;
- }
優化3,以最原始的傳值方式傳參,避免臨時物件建立:
[cpp] view plain copy print?- 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.
6. vector clear and swap trick
vector.clear並只是將size變數置為0,並沒有及時歸還OS,STL仍然持有記憶體,以便後續push_back。實測如下:
[cpp] view plain copy print?- vector<int> temp;
增長vector然後清空:
- temp.resize( 1024*1024*20 ); // 80M
- temp.clear();
clear以後程序兵沒有及時將記憶體歸還OS。。。通過swap方法:
[cpp] view plain copy print?- tmp.resize(1024*1024*20); // 80M
- // tmp.clear();
- {
- std::vector<int>().swap(tmp); // 將記憶體歸還OS
- }
附上網路版的String:
[cpp] view plain copy print?- #include <iostream>
- #include <cstring>
- usingnamespace std;
- class String
- {
- public:
- String();
- String(constchar *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;}
- constchar * GetString()const {return itsString;}
- private:
- String (unsigned short);
- char * itsString;
- unsigned short itsLen;
- };
- String::String()
- {
- itsString = newchar[1]; //為什麼設定成1,這樣會導致記憶體1bytes無法釋放嗎?我覺得和itsString = new char沒區別,那他為什麼要設定成1,這樣有什麼用?21天學會C++那本書,我也有 ,書上也確實是設定成1.
- itsString[0] = '/0';
- itsLen=0;
- }
- String::String(unsigned short len)
- {
- itsString = newchar[len+1];
- for (unsigned short i =0;i<=len;i++)
- itsString[i] = '/0';
- itsLen=len;
- }
- String::String(constchar * const cString)
- {
- itsLen = strlen(cString);
- itsString = newchar[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 = newchar[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 = newchar[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;
- }
參考引用:
C++ idioms
Copy and Swap idiom
History:
20140401 - add 6 vector clear and swap trick!