1 #include <QCoreApplication>
2 #include <iostream>
3 #include <vector>
4 #include <assert.h>
5
6 #define Has_Move
7 /*
8 * 话题:右值引用+移动语义+函数模板
9 * 一、右值引用
10 * 1. C++的引用允许你为已经存在的对象创建一个新的名字。对新引用所做的访问和修改操作,都会影响它的原型。
11 * 2. C++11之前只有左值引用。
12 * lvalue这个词来自于C语言,指的是可以放在赋值表达式左边的事物——在栈上或堆上分配的命名对象,或者其他对象成员——有明确的内存地址;
13 * rvalue这个词也来源于C语言,指的是可以出现在赋值表达式右侧的对象——例如,文字常量和临时变量。因此,左值引用只能被绑定在左值上,而不是右值。
14 *
15 * 3. 右值不可以赋值给左值引用, 例如: int &i = 27;
16 * 4. 右值可以赋值给左值的const引用, 例如:int const& i = 27; 也可以右值赋值给 const的左值引用 const int &i = 27;
17 * 5. C++11标准介绍了右值引用(rvalue reference),这种方式只能绑定右值,不能绑定左值。 例如:int&& i = 27;
18 * 6. 有个有趣的现象: 右值引用可以赋值给左值的const引用,也可以赋值给 const的左值引用,但是反过来赋值不可以。
19 *
20 * 二、移动语义
21 * 7. C++11新添语义——移动语义(move semantics)。
22 * 8. 右值通常都是临时的。
23 * 例如:函数返回值; 常量做参数传递;
24 * 9. 当传递一个右值时,不会发生对象的拷贝, 而是一种“移动”,或者说是一种“窃取”。
25 * 直接把对象从原来的所有者那里“窃取”过来,并且对象的所有状态不会发生改变。没有拷贝的发生,会节省很多的内存分配,性能更好。
26 * 例如:需要传递一个 std::vector<int>,值传递时,需要分配同等大小的内存空间。
27 * 10. C++标准库不会将一个对象显式的转移到另一个对象中,除非该对象将销毁的时候,或被赋值的时候(拷贝和移动的操作很相似)。
28 * 参考:问-思考:2
29 *
30 * 三、右值引用与函数模板
31 * 11. 当右值引用作为函数模板的参数时,实参既可以是左值,也可以是右值。
32 * 因右值引用形如:T&& t,在实参像形参匹配的过程中, 有可能匹配为 TT & t(TT=T&), 也可能匹配为 TT t(TT=T&&)。
33 * 因此会有这样的总结:如果函数模板的参数是右值引用,
34 * 当实参为左值时, 模板会把实参类型当作左值引用,即 TT & t(TT=T&);
35 * 当实参为右值时, 模板会把实参类型当作普通数据使用。 即 TT t(TT=T&&)
36 *
37 *
38 * 12. 很显然,左值移动 和 右值移动 可以作为函数重载。
39 * 构造函数和移动构造函数就能说明这一点。
40 *
41 * 13. 实践中移动能保证类中的所有状态保持不变,表现良好
42 *
43 * 实例场景:
44 * 1. 函数返回一个局部变量。
45 * 使用移动语义, 不再需要将局部变量拷贝一个副本给返回值。
46 * std::string, std::vector<> 都是具备移动构造函数和移动赋值运算符的,就是为了避免拷贝大量数据。
47 *
48 *
49 * 问-思考:
50 * 1. 移动前后,两个变量的地址相同吗?
51 * 答:不相同。
52 * 2. 一个类同时具备拷贝拷贝构造函数和移动构造函数时, 什么情况下会调用拷贝构造? 而什么情况下会调用移动构造呢?
53 * 答:对于函数结束前返回临时对象的情况。
54 * 若定义了移动构造函数,则不论返回时是否显示使用移动语义,都将调用移动构造函数;
55 * 若未显示定义移动构造函数, 则不论返回时是否显示使用移动语义,都将调用普通的构造函数。
56 *
57 * 对于局部对象构造局部对象的情况。
58 * 若定义了移动构造函数,则显示使用 std::move 时,调用移动构造函数,未显示使用 std::move 则调用普通的构造函数。
59 * 若未定义移动构造函数,则不论是否使用 std::move, 都将调用普通的构造函数。
60 */
61
62 //! [实例场景] -1
63 int func(){
64 int a = 10;
65 return std::move(a);
66 }
67
68 std::string func_1(){
69 std::string _s = "abcdefg";
70 return std::move(_s);
71 }
72
73 std::vector<int> func_2(){
74 std::vector<int> _v = {1, 2, 3, 4, 5};
75 return std::move(_v);
76 }
77
78 std::vector<std::string> func_3(){
79 std::vector<std::string> _vs = {"abc", "def", "ghi"};
80 return std::move(_vs);
81 }
82 //! [实例场景]
83
84
85 //! [问-思考] -1
86 std::string think_1(){
87 std::string _s = "abcdefg";
88 std::cout<<"In think_1 "<<_s<<" address is "<<&_s<<std::endl;
89 return std::move(_s);
90 }
91 std::string& think_1_1(){ //返回引用编译能通过,运行会崩溃。 问-接龙:为什么移动构造函数不会崩溃
92 std::string _s = "abcdefg";
93 std::cout<<"In think_1_1 "<<_s<<" address is "<<&_s<<std::endl;
94 return std::move(_s);
95 }
96 //! [问-思考]
97
98 //! [问-思考] -2
99 class MyClass{
100 public:
101 MyClass(){}
102 MyClass(MyClass & ){
103 std::cout<<"invoke MyClass(MyClass & other)"<<std::endl;
104 }
105 #ifdef Has_Move
106 MyClass(MyClass &&){
107 std::cout<<"invoke MyClass(MyClass &&other)"<<std::endl;
108 }
109 #endif
110 };
111 MyClass think_2(){
112 std::cout<<"In think_2"<<std::endl;
113
114 MyClass obj;
115 return obj; //可能调用移动构造
116 }
117 MyClass think_2_1(){
118 std::cout<<"In think_2_1"<<std::endl;
119
120 MyClass obj;
121 return std::move(obj); //可能调用移动构造
122 }
123
124 MyClass& think_2_2(){//返回引用编译能通过,运行会崩溃。
125 std::cout<<"In think_2_2"<<std::endl;
126
127 MyClass obj;
128 return std::move(obj);
129 }
130 //! [问-思考]
131
132 class MyArray{
133 public:
134 MyArray(char str[], int len):_str(new char[1000]){
135 std::cout<<"len "<<len<<std::endl;
136 std::copy(str, str+len, _str);
137 }
138 MyArray(MyArray & other):_str(new char[1000]){
139 std::cout<<"Invoke MyArray(MyArray & other)"<<std::endl;
140 std::copy(other._str, other._str+1000, _str);
141 }
142 MyArray(MyArray &&other):_str(other._str){
143 std::cout<<"Invoke MyArray(MyArray &&other)"<<std::endl;
144 other._str=0;
145 }
146
147 ~MyArray(){
148 delete[] _str;
149 }
150 void print(){
151 std::cout<<_str<<std::endl;
152 }
153
154 private:
155 char * _str;
156 };
157
158 int main(int argc, char *argv[])
159 {
160 QCoreApplication a(argc, argv);
161
162 //! [话题] -6
163 //int& _iref = 27; //error: C2440:"初始化":无法从"int"转换为"int&"
164 const int & _ciref = 27;
165 int const& _icref = 27;
166 std::cout<<"_ciref="<<_ciref<<std::endl;
167 std::cout<<"_icref="<<_icref<<std::endl;
168
169 int && _irref = 27;
170 //int && _irref1 = _ciref; //error: C2440:"初始化":无法从"const int"转换为"int&&"
171 //int && _irref2 = _icref; //error: C2440:"初始化":无法从"const int"转换为"int&&"
172 const int & _ciref1 = _irref; //! 特别注意 可以从 int&& 到 const int
173 int const & _icref1 = _irref; //! 特别注意 可以从 int&& 到 int const
174 std::cout<<"_ciref1="<<_ciref<<std::endl;
175 std::cout<<"_icref1="<<_icref<<std::endl;
176
177 std::cout<<"-----------------"<<std::endl<<std::endl;
178 //int const & 和 const int & 这两种类型相同吗?
179 //auto _type1 = typeid(_ciref);
180 //auto _type2 = typeid(_icref);
181 const std::type_info& _type1 = typeid(_ciref);
182 const std::type_info& _type2 = typeid(_icref);
183 std::cout<<"const int & _ciref, _ciref type is "<<_type1.name()<<std::endl; //两种都是 int 类型,有点扑朔迷离
184 std::cout<<"int const& _icref, _icref type is "<<_type2.name()<<std::endl; //两种都是 int 类型,有点扑朔迷离
185 //! [话题]
186
187 std::cout<<"-----------------"<<std::endl<<std::endl;
188
189 //! [实例场景] -1
190 std::cout<<func()<<std::endl;
191
192 std::cout<<func_1()<<std::endl;
193
194 std::vector<int> _v = func_2();
195 //std::copy(_v.begin(), _v.end(), &std::cout); //error info: C2697 二进制 "=":没有找到接受"int"类型的右操作数的运算符(或没有可接受的转换)
196 for (auto v : _v)
197 std::cout<<v;
198 std::cout<<std::endl;
199
200 std::vector<std::string> _vs = func_3();
201 //std::copy(_vs.begin(), _vs.end(), &std::cout); //error info: C2697 二进制 “=”:没有找到接受
202 //std::basic_string<char, std::char_traits<char>, std::allocator<char>>
203 //类型的右操作数的运算符(或没有可接受的转换)
204 for (auto s : _vs)
205 std::cout<<s;
206 std::cout<<std::endl;
207 //! [实例场景]
208
209 std::cout<<"-----------------"<<std::endl<<std::endl;
210
211 //! [问-思考] -1
212 std::string _s1 = think_1();
213 std::cout<<_s1<<" address is "<<&_s1<<std::endl;
214
215 //std::string _s1_1 = think_1_1(); //崩溃了
216 //std::string _s1_1(think_1_1()); //崩溃了
217 //std::cout<<_s1_1<<" address is "<<&_s1_1<<std::endl;
218
219 //! [问-思考]
220
221 //! [问-思考] -2
222 think_2();
223 think_2_1();
224 //think_2_2();
225
226 MyClass _obj;
227 MyClass _obj1(_obj); //普通普通构造
228 MyClass _obj2(std::move(_obj)); //可能调用移动构造
229 //! [问-思考]
230
231
232 std::cout<<"-----------------"<<std::endl<<std::endl;
233
234 char _str[8] = "abcdefg";
235 MyArray *_myStr = new MyArray(_str, sizeof(_str));
236 _myStr->print();
237
238 MyArray *_myStrL = new MyArray(*_myStr);
239 _myStrL->print();
240 //delete _myStr; _myStr = 0;
241 delete _myStrL; _myStrL = 0;
242
243 MyArray * _myStrR = new MyArray(std::move(*_myStr));
244 _myStrR->print();
245 delete _myStr; _myStr = 0;
246 delete _myStrR; _myStrR = 0;
247 //assert(1 == 2); 异常 条件不成立时异常
248 std::cout<<std::endl<<"------finish---"<<std::endl;
249 return a.exec();
250 }