zoukankan      html  css  js  c++  java
  • std::get<C++11多线程库>(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 }
  • 相关阅读:
    Golang里边的map变量是什么?
    Golang map的底层实现
    方法和函数的区别
    Golang 对 对象和指针 的理解
    React 部分
    前端开发概述、html、css基础
    服务器核心知识
    常用模块8.7
    2017.8.2迭代器和生成器
    2017.7.18可变/不可变类型,符号运算及其流程控制
  • 原文地址:https://www.cnblogs.com/azbane/p/15334391.html
Copyright © 2011-2022 走看看