zoukankan      html  css  js  c++  java
  • (转)谈谈C++中的swap函数

    转自:http://blog.csdn.net/ryfdizuo/article/details/6435847

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

    1. template <class T> void swap ( T& a, T& b )  
    2. {  
    3.   T c(a); a=b; b=c;  
    4. }  

     

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

    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::operator=进行拷贝赋值。。。所以建议优先使用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. }  

     

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

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

    1. String& String::operator =(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::operator =(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::operator = (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.  

     

    最后这张方式主要是对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::operator =(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::operator [](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::operator [](unsigned short offset)const  
    77. {  
    78.         if (offset > itsLen)  
    79.                 itsString[itsLen-1];  
    80.         else  
    81.                 return itsString[offset];  
    82. }  
    83. String String::operator +(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::operator +=(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.           
    110.         String s1("initial test"); //调用了什么函数?  
    111.         cout<<"S1:/t"<<s1.GetString()<<endl;  
    112.         char *temp ="Hello World";  
    113.         s1 = temp;//调用了什么函数?  
    114.         cout<<"S1:/t"<<s1.GetString()<<endl;  
    115.         char tempTwo[20];  
    116.         strcpy(tempTwo,"; nice to be here!");  
    117.         s1 += tempTwo;  
    118.         cout<<"tempTwo:/t"<<tempTwo<<endl;  
    119.         cout<<"S1:/t"<<s1.GetString()<<endl;  
    120.         cout<<"S1[4]:/t"<<s1[4]<<endl;  
    121.         cout<<"S1[999]:/t"<<s1[999]<<endl;//调用了什么函数?  
    122.         String s2(" Anoter string");//调用了什么函数?  
    123.         String s3;  
    124.         s3 = s1+s2;  
    125.         cout<<"S3:/t" <<s3.GetString()<<endl;  
    126.         String s4;  
    127.         s4 = "Why does this work?";//调用了什么函数?  
    128.         cout<<"S4:/t"<<s4.GetString()<<endl;  
    129.         return 0;  
    130. }  

     

     

    参考引用:

    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!

  • 相关阅读:
    Azure PowerShell (7) 使用CSV文件批量设置Virtual Machine Endpoint
    Windows Azure Cloud Service (39) 如何将现有Web应用迁移到Azure PaaS平台
    Azure China (7) 使用WebMetrix将Web Site发布至Azure China
    Microsoft Azure News(4) Azure新D系列虚拟机上线
    Windows Azure Cloud Service (38) 微软IaaS与PaaS比较
    Windows Azure Cloud Service (37) 浅谈Cloud Service
    Azure PowerShell (6) 设置单个Virtual Machine Endpoint
    Azure PowerShell (5) 使用Azure PowerShell创建简单的Azure虚拟机和Linux虚拟机
    功能代码(1)---通过Jquery来处理复选框
    案例1.用Ajax实现用户名的校验
  • 原文地址:https://www.cnblogs.com/xj2015/p/7383234.html
Copyright © 2011-2022 走看看