(转)谈谈C++中的swap函数

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

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

[cpp] view plain copy

print?

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

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

2,针对int型优化:

[cpp] view plain copy

print?

  1. void swap(int & __restrict a, int & __restrict b)
  2. {
  3. a ^= b;
  4. b ^= a;
  5. a ^= b;
  6. }

无需构造临时对象,异或

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

[cpp] view plain copy

print?

  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

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

[cpp] view plain copy

print?

  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

[cpp] view plain copy

print?

  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

[cpp] view plain copy

print?

  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交换,临时变量自动销毁~

[cpp] view plain copy

print?

  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函数。

[cpp] view plain copy

print?

  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& 传参防止临时对象。

[cpp] view plain copy

print?

  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) .

[cpp] view plain copy

print?

  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,不进行拷贝赋值,只是交换控制信息,而且是强异常安全:

[cpp] view plain copy

print?

  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,以最原始的传值方式传参,避免临时对象创建:

[cpp] view plain copy

print?

  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。实测如下

[cpp] view plain copy

print?

  1. vector<int> temp;

此时打开资源管理器,内存如下:


增长vector然后清空:

[cpp] view plain copy

print?

  1. temp.resize( 1024*1024*20 );    // 80M
  2. temp.clear();

此时资源管理器内存:

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

[cpp] view plain copy

print?

  1. tmp.resize(1024*1024*20);    // 80M
  2. // tmp.clear();
  3. {
  4. std::vector<int>().swap(tmp);        // 将内存归还OS
  5. }

退出作用域,临时对象销毁。内存归还OS。此时资源管理器中进程内存回到1,864K。

附上网络版的String:

[cpp] view plain copy

print?

  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. 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!

时间: 2024-10-17 15:13:40

(转)谈谈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

C++中的swap函数

最通用的模板交换函数模式:创建临时对象,调用对象的赋值操作符 template <class T> void swap ( T& a, T& b ) { T c(a); a=b; b=c; } 需要构建临时对象,一个拷贝构造,两次赋值操作. 针对int型优化 void swap(int & __restrict a, int & __restrict b) { a ^= b; b ^= a; a ^= b; } 无需构造临时对象,异或.

Effective C++笔记_条款25考虑写出一个不抛出异常的swap函数

1 // lib中的swap 2 namespace std { 3 template<typename T> 4 void swap (T& a, T& b) 5 { 6 T temp(a); 7 a = b; 8 b = temp; 9 } 10 } 11 12 // 缺点:需要赋值大量的数据,但是有的时候并不要复制如此多的内容 13 class WidgetImpl { 14 public: 15 //... 16 private: 17 int a, b, c; 18

谈谈c++中继承中的虚函数

c++继 承中的虚函数 c++是一种面向对象的编程语言的一个很明显的体现就是对继承机制的支持,c++中继承分很多种,按不同的分类有不同分类方法,比如可以按照基类的个数分为多继承和单继承,可以按照访问权限分为public继承.protected继承和private继承,按照是否是虚拟继承可以分为virtual继承和non-virtual继承.当然这里的分类标准都是有重叠的部分,比如,non-virtual继承又可以分为单继承和多继承.这里要讨论的是虚函数,因此主要从virtual和non-virt

C++中swap函数

本文是我用到swap函数时,对其产生好奇,所以结合网上有关博文写下的.个人水平有限,若有错误的地方,欢迎留言指出.谢谢! 一.通用的函数交换模板 1 template<class T> 2 void swap(T &a,T &b) 3 { 4 T c(a); 5 a=b; 6 b=c; 7 } T为类型,可根据实际需要使用相应的类型 二.针对int类型的优化 使用异或,整数异或本身为结果0:一个数异或0结果为本身: 1 void swap(int &a,int &

C++中的swap(交换函数)

交换两个变量的值很简单. 比如 int a = 1; b = 2; 交换a b的值 这个很简单 很容易想到的是找个中间变量比如  int temp  = a; a = b; b = temp; 不需要中间变量可不可以? 当然是可以的. 比如 [加减法] a = a + b; b = a - b; a = a - b; 该方法可以交换整型和浮点型数值的变量,但在处理浮点型的时候有可能出现精度的损失,例如对数据: a = 3.123456 b = 1234567.000000 交换后各变量值变为:

《Effective C 》资源管理:条款25--考虑写出一个不抛出异常的swap函数

条款25考虑写出一个不抛出异常的swap函数 条款25:考虑写出一个不抛出异常的swap函数 swap是STL中的标准函数,用于交换两个对象的数值.后来swap成为异常安全编程(exception-safe programming,条款29)的脊柱,也是实现自我赋值(条款11)的一个常见机制.swap的实现如下: namespace std{ template<typename T> void swap(T& a, T& b) { T temp(a); a=b; b=temp;

条款25:考虑写出一个不抛异常的swap函数

条款25:考虑写出一个不抛异常的swap函数 swap函数在C++中是一个非常重要的函数,但实现也非常复杂. 看一个缺省的std::swap函数的实现 namespace std { template<typename T> void swap( T& a , T& b) { T temp(a); a = b; b = temp } } ①内置类型的调用 int a = 2; int b =3; std::swap(a, b); cout<<"a:&quo

作业二、comp和swap函数

一.swap函数的代码及运行情况 1.代码 1 #include<stdio.h> 2 int main() 3 { 4 void swap(int *m,int *n); 5 int a,b; 6 int *p1,*p2; 7 scanf("%d,%d",&a,&b); 8 p1=&a; 9 p2=&b; 10 swap(p1,p2); 11 printf("%d,%d\n",*p1,*p2); 12 return 0;