std::get(05): 右值引用–移动语义–函数模板

  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 }

 

原文链接: https://www.cnblogs.com/azbane/p/15334391.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    std::get<C++11多线程库>(05): 右值引用--移动语义--函数模板

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/398313

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月12日 上午9:37
下一篇 2023年4月12日 上午9:37

相关推荐