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:
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:
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 & ;) ;
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: :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!