• 【转】 谈谈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: :o 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: :o 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 &amp ;) ;  
     10                 ~String();  
     11                 char & operator[] (unsigned short offset);  
     12                 char operator[] (unsigned short offset)const;  
     13                 String operator+(const String&amp ;) ;  
     14                 void operator+=(const String&amp ;) ;  
     15                 String & operator= (const String &amp ;) ;  
     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

  • 相关阅读:
    DOM_节点操作创建表格
    表单提交
    HTML常用标签
    网络通讯详解
    java===TCP(多线程多客户端同时上传字节数据:图片为例)
    java===TCP(文件上传功能)
    java===UDP
    java==IO=随机读写文件
    git中的基本命令
    ansible中roles的简单使用
  • 原文地址:https://www.cnblogs.com/davygeek/p/4211562.html
Copyright © 2020-2023  润新知