• 左值引用、右值引用和移动语义


    【1】左值引用和右值引用

    左值引用(一般所谓的引用形式)使标识符关联到左值。

    何为左值?左值是一个表示数据的表达式(如变量名、解除引用的指针)。

    最初,左值可出现在赋值语句的左边,但修饰符const的出现使得可以声明这样的标识符(即不能给它赋值,但可获取其地址)。

     1     int n = 10;
     2     int * pt = new int;
     3     int & rn = n;  // 变量名
     4     int & rt = *pt;  // 解除引用的指针
     5     const int b = 20;  // 不可以给b赋值,但可获取其地址
     6     // b = 21;  // error
     7     const int * pb = &b;  // ok
     8     const int & rb = b;  // 不可以给rb赋值,但可获取其地址
     9     // rb = n;    // error
    10     const int * prb = &rb;  // ok

    总而言之,判断左值的唯一条件是程序可获取其地址(即可对其应用地址运算符)。

    右值引用使标识符关联到右值。右值引用是使用&&表示的。右值即可出现在赋值表达式右边,但不能对其应用地址运算符的值(与左值相反)。

    右值包括字面常量(C-风格字符串除外,因为它表示地址),比如x+y等表达式以及返回值的函数(条件是该函数返回的不是引用)。

    举例如下:

    1     int x = 10;
    2     int y = 11;
    3     int && r1 = 12;  // 常量
    4     int && r2 = x + y;  // 表达式
    5     double && r3 = std::sqrt(2.0); // 函数返回值

    通过示例可以发现:将右值关联到右值引用导致该右值被存储到特定的位置,并且可通过标识符获取该位置的地址。也就是说,虽然不可将地址运算符&应用于12,但可将其用于r1。将数据与特定的地址关联,使得可通过右值引用来访问该数据信息。

    下面演示及验证右值引用的作用:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 double f(double tf) { return tf/20; };
     5 
     6 void main()
     7 {
     8     double tc = 10.5;
     9     double && rd1 = 100.01;
    10     double && rd2 = 1.8 * tc;
    11     double && rd3 = f(rd2);
    12     cout << "tc Value And Address: " << tc << "  " << &tc << endl;
    13     cout << "rd1 Value And Address: " << rd1 << " " << &rd1 << endl;
    14     cout << "rd2 Value And Address: " << rd2 << "  " << &rd2 << endl;
    15     cout << "rd3 Value And Address: " << rd3 << "  " << &rd3 << endl;
    16     cin.get();
    17 }
    18 // run out
    19 /*
    20 tc Value And Address: 10.5  003FFAE4
    21 rd1 Value And Address: 100.01 003FFAC8
    22 rd2 Value And Address: 18.9  003FFAAC
    23 rd3 Value And Address: 0.945  003FFA90
    24 */

    为啥需要右值引用呢?引入右值引用的主要目的之一是实现移动语义。

    【2】移动语义之移动构造函数

    2.1 为何需要移动语义?

    请看如下示例代码(相信你我已经写过很多类似的),只有复制构造函数情况下:

      1 // 例1:只有复制构造函数
      2 #include <iostream>
      3 using namespace std;
      4 
      5 // interface
      6 class Useless
      7 {
      8 private:
      9     int n;          // number of elements
     10     char * pc;      // pointer to data
     11     static int ct;  // number of objects
     12     void ShowObject() const;
     13 
     14 public:
     15     Useless();
     16     explicit Useless(int k);
     17     Useless(int k, char ch);
     18     Useless(const Useless & f); // regular copy constructor
     19     ~Useless();
     20     Useless operator+(const Useless & f)const;
     21     void ShowData() const;
     22 };
     23 
     24 // implementation
     25 int Useless::ct = 0;
     26 
     27 Useless::Useless()
     28 {
     29     ++ct;
     30     n = 0;
     31     pc = nullptr;
     32     cout << "default constructor called; number of objects: " << ct << endl;
     33     ShowObject();
     34 }
     35 
     36 Useless::Useless(int k) : n(k)
     37 {
     38     ++ct; 
     39     cout << "Useless(int k) constructor called; number of objects: " << ct << endl;
     40     pc = new char[n];
     41     ShowObject();
     42 }
     43 
     44 Useless::Useless(int k, char ch) : n(k)
     45 {
     46     ++ct;
     47     cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl;
     48     pc = new char[n];
     49     for (int i = 0; i < n; i++)
     50         pc[i] = ch;
     51     ShowObject();
     52 }
     53 
     54 Useless::Useless(const Useless & f) : n(f.n) 
     55 {
     56     ++ct;
     57     cout << "copy constructor const called; number of objects: " << ct << endl;
     58     pc = new char[n];
     59     for (int i = 0; i < n; i++)
     60         pc[i] = f.pc[i];
     61     ShowObject();
     62 }
     63 
     64 Useless::~Useless()
     65 {
     66     cout << "destructor called; "; 
     67     cout << "deleted object:
    ";
     68     ShowObject();
     69     delete [] pc;
     70     cout << "objects left: " << --ct << endl << endl;
     71 }
     72 
     73 Useless Useless::operator+(const Useless & f)const
     74 {
     75     cout << "Entering operator+()
    ";
     76     Useless temp = Useless(n + f.n);
     77     for (int i = 0; i < n; i++)
     78         temp.pc[i] = pc[i];
     79     for (int i = n; i < temp.n; i++)
     80         temp.pc[i] = f.pc[i - n];
     81     cout << "temp object:
    ";
     82     cout << "Leaving operator+()
    ";
     83     return temp;
     84 }
     85 
     86 void Useless::ShowObject() const
     87 { 
     88     cout << "Number of elements: " << n;
     89     cout << " Data address: " << (void *) pc << endl << endl;
     90 }
     91 
     92 void Useless::ShowData() const
     93 {
     94     if (0 == n)
     95     {
     96         cout << "(object empty)";
     97     }
     98     else
     99     {
    100         for (int i = 0; i < n; i++)
    101             cout << pc[i];
    102     }
    103     cout << endl;
    104 }
    105 
    106 // application
    107 int main()
    108 {
    109     {
    110         Useless one(10, 'x');
    111         Useless two = one;          // calls copy constructor
    112         Useless three(20, 'o');
    113         Useless four(one + three);  // calls operator+(), copy constructor
    114         cout << "object one: ";
    115         one.ShowData();
    116         cout << "object two: ";
    117         two.ShowData();
    118         cout << "object three: ";
    119         three.ShowData();
    120         cout << "object four: ";
    121         four.ShowData();
    122     }
    123     cin.get();
    124 }
    125 
    126 // out
    127 /*
    128 Useless(int k, char ch) constructor called; number of objects: 1
    129 Number of elements: 10 Data address: 004A4910
    130 
    131 copy constructor const called; number of objects: 2
    132 Number of elements: 10 Data address: 004A4958
    133 
    134 Useless(int k, char ch) constructor called; number of objects: 3
    135 Number of elements: 20 Data address: 004A49A0
    136 
    137 Entering operator+()
    138 Useless(int k) constructor called; number of objects: 4
    139 Number of elements: 30 Data address: 004A49F0
    140 
    141 temp object:
    142 Leaving operator+()
    143 copy constructor const called; number of objects: 5
    144 Number of elements: 30 Data address: 004A4C50
    145 
    146 destructor called; deleted object:
    147 Number of elements: 30 Data address: 004A49F0
    148 
    149 objects left: 4
    150 
    151 object one: xxxxxxxxxx
    152 object two: xxxxxxxxxx
    153 object three: oooooooooooooooooooo
    154 object four: xxxxxxxxxxoooooooooooooooooooo
    155 destructor called; deleted object:
    156 Number of elements: 30 Data address: 004A4C50
    157 
    158 objects left: 3
    159 
    160 destructor called; deleted object:
    161 Number of elements: 20 Data address: 004A49A0
    162 
    163 objects left: 2
    164 
    165 destructor called; deleted object:
    166 Number of elements: 10 Data address: 004A4958
    167 
    168 objects left: 1
    169 
    170 destructor called; deleted object:
    171 Number of elements: 10 Data address: 004A4910
    172 
    173 objects left: 0
    174 
    175 */

    运行结果分析如下:

    从运算符+重载开始琢磨,如下:

     1 Useless Useless::operator+(const Useless & f)const
     2 {
     3     cout << "Entering operator+()
    ";
     4     Useless temp = Useless(n + f.n);
     5     for (int i = 0; i < n; i++)
     6         temp.pc[i] = pc[i];
     7     for (int i = n; i < temp.n; i++)
     8         temp.pc[i] = f.pc[i - n];
     9     cout << "temp object:
    ";
    10     cout << "Leaving operator+()
    ";
    11     return temp;
    12 }

    运算符+重载实现中,创建了一个局部对象temp(注意与临时对象的区别),最终函数结束时返回该对象。

    由于函数返回值类型,所以这里只能调用复制构造函数来创建对象four。如下:

    1 Useless::Useless(const Useless & f) : n(f.n) 
    2 {
    3     ++ct;
    4     cout << "copy constructor const called; number of objects: " << ct << endl;
    5     pc = new char[n];
    6     for (int i = 0; i < n; i++)
    7         pc[i] = f.pc[i];
    8     ShowObject();
    9 }

    因此复制构造函数的形参f直接指向该局部对象(即实参),通过另开辟空间、深拷贝该局部对象从而完成创建对象four的任务。

    然后,析构掉该局部对象。关键这里白白析构掉这个局部对象申请的内存空间的确有点太奢侈了,尤其从时间方面权衡整个过程。

    如果,有一种构造函数可让对象four的成员变量pc指针直接指向该局部对象已开辟的内存空间,而不需用再像复制构造函数那样又另开辟空间来创建对象four。

    那么,这个过程将节省多少时间呢?这里要点是做了大量的无用功。即就是说,将临时对象的所有权直接交给对象four,不是更完美吗?

    2.2 移动语义是什么?

    请看如下示例,增加移动构造函数:

      1 // 例2:复制构造函数 与 移动构造函数
      2 // useless.cpp -- an otherwise useless class with move semantics
      3 #include <iostream>
      4 using namespace std;
      5 
      6 // interface
      7 class Useless
      8 {
      9 private:
     10     int n;          // number of elements
     11     char * pc;      // pointer to data
     12     static int ct;  // number of objects
     13     void ShowObject() const;
     14 
     15 public:
     16     Useless();
     17     explicit Useless(int k);
     18     Useless(int k, char ch);
     19     Useless(const Useless & f); // regular copy constructor
     20     Useless(Useless && f);      // move constructor
     21     Useless & operator=(const Useless & f); // copy assignment
     22     Useless & operator=(Useless && f); // move assignment
     23     ~Useless();
     24     Useless operator+(const Useless & f) const;
     25     void ShowData() const;
     26 };
     27 
     28 // implementation
     29 int Useless::ct = 0;
     30 
     31 Useless::Useless()
     32 {
     33     ++ct;
     34     n = 0;
     35     pc = nullptr;
     36     cout << "default constructor called; number of objects: " << ct << endl;
     37     ShowObject();
     38 }
     39 
     40 Useless::Useless(int k) : n(k)
     41 {
     42     ++ct; 
     43     cout << "Useless(int k) constructor called; number of objects: " << ct << endl;
     44     pc = new char[n];
     45     ShowObject();
     46 }
     47 
     48 Useless::Useless(int k, char ch) : n(k)
     49 {
     50     ++ct;
     51     cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl;
     52     pc = new char[n];
     53     for (int i = 0; i < n; i++)
     54         pc[i] = ch;
     55     ShowObject();
     56 }
     57 
     58 Useless::Useless(const Useless & f) : n(f.n) 
     59 {
     60     ++ct;
     61     cout << "copy constructor const called; number of objects: " << ct << endl;
     62     pc = new char[n];
     63     for (int i = 0; i < n; i++)
     64         pc[i] = f.pc[i];
     65     ShowObject();
     66 }
     67 
     68 Useless::Useless(Useless && f) : n(f.n) 
     69 {
     70     ++ct;
     71     cout << "move constructor called; number of objects: " << ct << endl;
     72     pc = f.pc;       // steal address
     73     f.pc = nullptr;  // give old object nothing in return
     74     f.n = 0;
     75     ShowObject();
     76 }
     77 
     78 Useless & Useless::operator=(const Useless & f)
     79 {
     80     if (this == &f)
     81         return *this;
     82     delete []pc;
     83     n = f.n;
     84     pc = new char[n];
     85     for (int i = 0; i < n; ++i)
     86         pc[i] = f.pc[i];
     87     return *this;
     88 }
     89 
     90 Useless & Useless::operator=(Useless && f)
     91 {
     92     if (this == &f)
     93         return *this;
     94     delete []pc;
     95     n = f.n;
     96     pc = f.pc;
     97     f.n = 0;
     98     f.pc = nullptr;
     99     return *this;
    100 }
    101 
    102 Useless::~Useless()
    103 {
    104     cout << "destructor called; "; 
    105     cout << "deleted object:
    ";
    106     ShowObject();
    107     delete [] pc;
    108     cout << "objects left: " << --ct << endl << endl;
    109 }
    110 
    111 Useless Useless::operator+(const Useless & f)const
    112 {
    113     cout << "Entering operator+()
    ";
    114     Useless temp = Useless(n + f.n);
    115     for (int i = 0; i < n; i++)
    116         temp.pc[i] = pc[i];
    117     for (int i = n; i < temp.n; i++)
    118         temp.pc[i] = f.pc[i - n];
    119     cout << "temp object:
    ";
    120     cout << "Leaving operator+()
    ";
    121     return temp;
    122 }
    123 
    124 void Useless::ShowObject() const
    125 { 
    126     cout << "Number of elements: " << n;
    127     cout << " Data address: " << (void *) pc << endl << endl;
    128 }
    129 
    130 void Useless::ShowData() const
    131 {
    132     if (0 == n)
    133     {
    134         cout << "(object empty)";
    135     }
    136     else
    137     {
    138         for (int i = 0; i < n; i++)
    139             cout << pc[i];
    140     }
    141     cout << endl;
    142 }
    143 
    144 // application
    145 int main()
    146 {
    147     {
    148         Useless one(10, 'x');
    149         Useless two = one;          // calls copy constructor
    150         Useless three(20, 'o');
    151         Useless four(one + three);  // calls operator+(), move constructor
    152         cout << "object one: ";
    153         one.ShowData();
    154         cout << "object two: ";
    155         two.ShowData();
    156         cout << "object three: ";
    157         three.ShowData();
    158         cout << "object four: ";
    159         four.ShowData();
    160     }
    161     cin.get();
    162 }
    163 
    164 // out
    165 /*
    166 Useless(int k, char ch) constructor called; number of objects: 1
    167 Number of elements: 10 Data address: 00224910
    168 
    169 copy constructor const called; number of objects: 2
    170 Number of elements: 10 Data address: 00224958
    171 
    172 Useless(int k, char ch) constructor called; number of objects: 3
    173 Number of elements: 20 Data address: 002249A0
    174 
    175 Entering operator+()
    176 Useless(int k) constructor called; number of objects: 4
    177 Number of elements: 30 Data address: 002249F0
    178 
    179 temp object:
    180 Leaving operator+()
    181 move constructor called; number of objects: 5
    182 Number of elements: 30 Data address: 002249F0
    183 
    184 destructor called; deleted object:
    185 Number of elements: 0 Data address: 00000000
    186 
    187 objects left: 4
    188 
    189 object one: xxxxxxxxxx
    190 object two: xxxxxxxxxx
    191 object three: oooooooooooooooooooo
    192 object four: xxxxxxxxxxoooooooooooooooooooo
    193 destructor called; deleted object:
    194 Number of elements: 30 Data address: 002249F0
    195 
    196 objects left: 3
    197 
    198 destructor called; deleted object:
    199 Number of elements: 20 Data address: 002249A0
    200 
    201 objects left: 2
    202 
    203 destructor called; deleted object:
    204 Number of elements: 10 Data address: 00224958
    205 
    206 objects left: 1
    207 
    208 destructor called; deleted object:
    209 Number of elements: 10 Data address: 00224910
    210 
    211 objects left: 0
    212 
    213 */

    运行结果分析如下:

    注意:上面分析运行结果图中,把temp称为局部变量,这次运行结果分析图中,把temp称为了局部对象。

    实质上,变量和对象是一回事,只不过人们习惯上把用内置类型定义的东东称为变量,而把自定义类型定义的东东称为对象(与类相匹配)。

    通过实践,我们发现:移动构造函数可以完美实现夙愿。

    从运行结果看到内存地址002249F0出现了三次:首次出现在构建局部对象时,二次出现在调用移动构造函数创建对象four时,第三次出现在析构对象four时。

    很显然,对象four的创建通过调用移动构造函数而没有再另申请内存空间,仅仅只是改变了局部对象实质内容的所有权而已。

    而观察局部对象析构时的打印信息:其指针成员值为空。目的为了防止对同一块内存多次delete引起程序的致命性错误。因为对空指针多次delete没有任何意义。

    如何理解程序中就多次delete同一块内存了呢?

    第一次,析构局部对象temp,其实释放的正是内存002249F0,只不过在析构之前移动构造函数将其已移动完成并置为空。

    第二次,析构对象four,释放的也是内存002249F0。(还没有看懂的话,可对比上面只有复制构造函数的程序运行结果再琢磨琢磨其中的套路。)

    总结:这个过程类似于在计算机中移动文件的情形:实际文件还留在原来的地方,只不过仅仅修改记录而已,这种方法被称为移动语义。

    实质上,移动语义即不移动原始数据,仅仅修改了记录(指针指向)而已。

    2.3 C++11如何支持移动语义?

    要实现移动语义,需要采取某种方式,让编译器知道什么时候需要复制,什么时候不需要,而这正是右值引用发挥作用的地方。

    可定义两个构造函数,其中一个是常规复制构造函数,它使用const左值引用作为参数,这个引用关联到左值实参;

    另一个是移动构造函数,它使用右值引用作为参数,该引用关联到右值实参。复制构造函数可执行深复制,而移动构造函数只调整记录。

    在将所有权转移给新对象的过程中,移动构造函数可能修改其实参,这意味着右值引用参数不应用const修饰。

    虽然使用右值引用可支持移动语义,但这并不会神奇的发生。要让移动语义发生,需要两个步骤:

    1. 右值引用让编译器知道何时可使用移动语义。

    比如,上面的示例中对象one是左值,与左值引用匹配,而表达式one + three是右值,与右值引用匹配。

    2. 为自定义类编写移动构造函数,使其具备所需的行为能力。

    【3】移动语义之移动赋值运算符

    适用于构造函数的移动语义考虑也适用于赋值运算符。

    示例1. 只有赋值运算符函数

      1 // 只有复制赋值运算符
      2 #include <iostream>
      3 using namespace std;
      4 
      5 // interface
      6 class Useless
      7 {
      8 private:
      9     int n;          // number of elements
     10     char * pc;      // pointer to data
     11     static int ct;  // number of objects
     12     void ShowObject() const;
     13 
     14 public:
     15     Useless();
     16     explicit Useless(int k);
     17     Useless(int k, char ch);
     18     Useless(const Useless & f); // regular copy constructor
     19     Useless(Useless && f);      // move constructor
     20     Useless & operator=(const Useless & f); // copy assignment
     21     ~Useless();
     22     Useless operator+(const Useless & f)const;
     23     void ShowData() const;
     24 };
     25 
     26 // implementation
     27 int Useless::ct = 0;
     28 
     29 Useless::Useless()
     30 {
     31     ++ct;
     32     n = 0;
     33     pc = nullptr;
     34     cout << "default constructor called; number of objects: " << ct << endl;
     35     ShowObject();
     36 }
     37 
     38 Useless::Useless(int k) : n(k)
     39 {
     40     ++ct; 
     41     cout << "Useless(int k) constructor called; number of objects: " << ct << endl;
     42     pc = new char[n];
     43     ShowObject();
     44 }
     45 
     46 Useless::Useless(int k, char ch) : n(k)
     47 {
     48     ++ct;
     49     cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl;
     50     pc = new char[n];
     51     for (int i = 0; i < n; i++)
     52         pc[i] = ch;
     53     ShowObject();
     54 }
     55 
     56 Useless::Useless(const Useless & f) : n(f.n) 
     57 {
     58     ++ct;
     59     cout << "copy const called; number of objects: " << ct << endl;
     60     pc = new char[n];
     61     for (int i = 0; i < n; i++)
     62         pc[i] = f.pc[i];
     63     ShowObject();
     64 }
     65 
     66 Useless::Useless(Useless && f) : n(f.n) 
     67 {
     68     ++ct;
     69     cout << "move constructor called; number of objects: " << ct << endl;
     70     pc = f.pc;       // steal address
     71     f.pc = nullptr;  // give old object nothing in return
     72     f.n = 0;
     73     ShowObject();
     74 }
     75 
     76 Useless & Useless::operator=(const Useless & f)
     77 {
     78     cout << "copy assignment operator= called;
    ";
     79     if (this == &f)
     80         return *this;
     81     delete []pc;
     82     n = f.n;
     83     pc = new char[n];
     84     for (int i = 0; i < n; ++i)
     85         pc[i] = f.pc[i];
     86     return *this;
     87 }
     88 
     89 Useless::~Useless()
     90 {
     91     cout << "destructor called; deleted object: "; 
     92     ShowObject();
     93     delete [] pc;
     94     cout << "objects left: " << --ct << endl;
     95 }
     96 
     97 Useless Useless::operator+(const Useless & f)const
     98 {
     99     cout << "Entering operator+()
    ";
    100     Useless temp = Useless(n + f.n);
    101     for (int i = 0; i < n; i++)
    102         temp.pc[i] = pc[i];
    103     for (int i = n; i < temp.n; i++)
    104         temp.pc[i] = f.pc[i - n];
    105     cout << "temp object:
    ";
    106     cout << "Leaving operator+()
    ";
    107     return temp;
    108 }
    109 
    110 void Useless::ShowObject() const
    111 { 
    112     cout << "Number of elements: " << n;
    113     cout << " Data address: " << (void *) pc << endl;
    114 }
    115 
    116 void Useless::ShowData() const
    117 {
    118     if (0 == n)
    119     {
    120         cout << "(object empty)";
    121     }
    122     else
    123     {
    124         for (int i = 0; i < n; i++)
    125             cout << pc[i];
    126     }
    127     cout << endl;
    128 }
    129 
    130 // application
    131 int main()
    132 {
    133     {
    134         Useless one(10, 'x');
    135         Useless two = one + one;  // calls move constructor
    136         cout << "Object one: ";
    137         one.ShowData(); 
    138         cout << "Object two: ";
    139         two.ShowData();
    140         Useless three, four;
    141         cout << "three = one 
    ";
    142         three = one;           // automatic copy assignment 
    143         cout << "Now Object three = ";
    144         three.ShowData();
    145         cout << "And Object one = ";
    146         one.ShowData();
    147         cout << "four = one + two 
    ";
    148         four = one + two;       // automatic move assignment
    149         cout << "Now Object four = ";
    150         four.ShowData();
    151         cout << "four = move(one)
    ";
    152         four = std::move(one);   // forced move assignment
    153         cout << "Now Object four = ";
    154         four.ShowData();
    155         cout << "And Object one = ";
    156         one.ShowData();
    157     }
    158     cin.get();
    159 }
    160 
    161 // run out
    162 /*
    163 Useless(int k, char ch) constructor called; number of objects: 1
    164 Number of elements: 10 Data address: 00794910
    165 Entering operator+()
    166 Useless(int k) constructor called; number of objects: 2
    167 Number of elements: 20 Data address: 00794958
    168 temp object:
    169 Leaving operator+()
    170 move constructor called; number of objects: 3
    171 Number of elements: 20 Data address: 00794958
    172 destructor called; deleted object: Number of elements: 0 Data address: 00000000
    173 objects left: 2
    174 Object one: xxxxxxxxxx
    175 Object two: xxxxxxxxxxxxxxxxxxxx
    176 default constructor called; number of objects: 3
    177 Number of elements: 0 Data address: 00000000
    178 default constructor called; number of objects: 4
    179 Number of elements: 0 Data address: 00000000
    180 three = one
    181 copy assignment operator= called;
    182 Now Object three = xxxxxxxxxx
    183 And Object one = xxxxxxxxxx
    184 four = one + two
    185 Entering operator+()
    186 Useless(int k) constructor called; number of objects: 5
    187 Number of elements: 30 Data address: 007949F0
    188 temp object:
    189 Leaving operator+()
    190 move constructor called; number of objects: 6
    191 Number of elements: 30 Data address: 007949F0
    192 destructor called; deleted object: Number of elements: 0 Data address: 00000000
    193 objects left: 5
    194 copy assignment operator= called;
    195 destructor called; deleted object: Number of elements: 30 Data address: 007949F0
    196 
    197 objects left: 4
    198 Now Object four = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    199 four = move(one)
    200 copy assignment operator= called;
    201 Now Object four = xxxxxxxxxx
    202 And Object one = xxxxxxxxxx
    203 destructor called; deleted object: Number of elements: 10 Data address: 007949F0
    204 
    205 objects left: 3
    206 destructor called; deleted object: Number of elements: 10 Data address: 007949A8
    207 
    208 objects left: 2
    209 destructor called; deleted object: Number of elements: 20 Data address: 00794958
    210 
    211 objects left: 1
    212 destructor called; deleted object: Number of elements: 10 Data address: 00794910
    213 
    214 objects left: 0
    215 
    216 */

    由于没有写移动赋值运算符函数,以上执行结果不做分析(可对比下面的分析更充分的理解)。

    示例2. 有赋值运算符函数,有移动赋值运算符函数

      1 // 有复制赋值运算符 且 有移动赋值运算符
      2 // useless.cpp -- an otherwise useless class with move semantics
      3 #include <iostream>
      4 using namespace std;
      5 
      6 // interface
      7 class Useless
      8 {
      9 private:
     10     int n;          // number of elements
     11     char * pc;      // pointer to data
     12     static int ct;  // number of objects
     13     void ShowObject() const;
     14 
     15 public:
     16     Useless();
     17     explicit Useless(int k);
     18     Useless(int k, char ch);
     19     Useless(const Useless & f); // regular copy constructor
     20     Useless(Useless && f);      // move constructor
     21     Useless & operator=(const Useless & f); // copy assignment
     22     Useless & operator=(Useless && f); // move assignment
     23     ~Useless();
     24     Useless operator+(const Useless & f)const;
     25     void ShowData() const;
     26 };
     27 
     28 // implementation
     29 int Useless::ct = 0;
     30 
     31 Useless::Useless()
     32 {
     33     ++ct;
     34     n = 0;
     35     pc = nullptr;
     36     cout << "default constructor called; number of objects: " << ct << endl;
     37     ShowObject();
     38 }
     39 
     40 Useless::Useless(int k) : n(k)
     41 {
     42     ++ct; 
     43     cout << "Useless(int k) constructor called; number of objects: " << ct << endl;
     44     pc = new char[n];
     45     ShowObject();
     46 }
     47 
     48 Useless::Useless(int k, char ch) : n(k)
     49 {
     50     ++ct;
     51     cout << "Useless(int k, char ch) constructor called; number of objects: " << ct << endl;
     52     pc = new char[n];
     53     for (int i = 0; i < n; i++)
     54         pc[i] = ch;
     55     ShowObject();
     56 }
     57 
     58 Useless::Useless(const Useless & f) : n(f.n) 
     59 {
     60     ++ct;
     61     cout << "copy const called; number of objects: " << ct << endl;
     62     pc = new char[n];
     63     for (int i = 0; i < n; i++)
     64         pc[i] = f.pc[i];
     65     ShowObject();
     66 }
     67 
     68 Useless::Useless(Useless && f) : n(f.n) 
     69 {
     70     ++ct;
     71     cout << "move constructor called; number of objects: " << ct << endl;
     72     pc = f.pc;       // steal address
     73     f.pc = nullptr;  // give old object nothing in return
     74     f.n = 0;
     75     ShowObject();
     76 }
     77 
     78 Useless & Useless::operator=(const Useless & f)
     79 {
     80     cout << "copy assignment operator= called;
    ";
     81     if (this == &f)
     82         return *this;
     83     delete []pc;
     84     n = f.n;
     85     pc = new char[n];
     86     for (int i = 0; i < n; ++i)
     87         pc[i] = f.pc[i];
     88     return *this;
     89 }
     90 
     91 Useless & Useless::operator=(Useless && f)
     92 {
     93     cout << "move assignment operator= called;
    ";
     94     if (this == &f)
     95         return *this;
     96     delete []pc;
     97     n = f.n;
     98     pc = f.pc;
     99     f.n = 0;
    100     f.pc = nullptr;
    101     return *this;
    102 }
    103 
    104 Useless::~Useless()
    105 {
    106     cout << "destructor called; deleted object: "; 
    107     ShowObject();
    108     delete [] pc;
    109     cout << "objects left: " << --ct << endl;
    110 }
    111 
    112 Useless Useless::operator+(const Useless & f)const
    113 {
    114     cout << "Entering operator+()
    ";
    115     Useless temp = Useless(n + f.n);
    116     for (int i = 0; i < n; i++)
    117         temp.pc[i] = pc[i];
    118     for (int i = n; i < temp.n; i++)
    119         temp.pc[i] = f.pc[i - n];
    120     cout << "temp object:
    ";
    121     cout << "Leaving operator+()
    ";
    122     return temp;
    123 }
    124 
    125 void Useless::ShowObject() const
    126 { 
    127     cout << "Number of elements: " << n;
    128     cout << " Data address: " << (void *) pc << endl;
    129 }
    130 
    131 void Useless::ShowData() const
    132 {
    133     if (0 == n)
    134     {
    135         cout << "(object empty)";
    136     }
    137     else
    138     {
    139         for (int i = 0; i < n; i++)
    140             cout << pc[i];
    141     }
    142     cout << endl;
    143 }
    144 
    145 // application
    146 int main()
    147 {
    148     {
    149         Useless one(10, 'x');
    150         Useless two = one + one;  // calls move constructor
    151         cout << "Object one: ";
    152         one.ShowData(); 
    153         cout << "Object two: ";
    154         two.ShowData();
    155         Useless three, four;
    156         cout << "three = one 
    ";
    157         three = one;           // automatic copy assignment 
    158         cout << "Now Object three = ";
    159         three.ShowData();
    160         cout << "And Object one = ";
    161         one.ShowData();
    162         cout << "four = one + two 
    ";
    163         four = one + two;       // automatic move assignment
    164         cout << "Now Object four = ";
    165         four.ShowData();
    166         cout << "four = move(one)
    ";
    167         four = std::move(one);   // forced move assignment
    168         cout << "Now Object four = ";
    169         four.ShowData();
    170         cout << "And Object one = ";
    171         one.ShowData();
    172     }
    173     cin.get();
    174 }
    175 /*
    176 Useless(int k, char ch) constructor called; number of objects: 1
    177 Number of elements: 10 Data address: 00204910
    178 Entering operator+()
    179 Useless(int k) constructor called; number of objects: 2
    180 Number of elements: 20 Data address: 00204958
    181 temp object:
    182 Leaving operator+()
    183 move constructor called; number of objects: 3
    184 Number of elements: 20 Data address: 00204958
    185 destructor called; deleted object: Number of elements: 0 Data address: 00000000
    186 objects left: 2
    187 Object one: xxxxxxxxxx
    188 Object two: xxxxxxxxxxxxxxxxxxxx
    189 default constructor called; number of objects: 3
    190 Number of elements: 0 Data address: 00000000
    191 default constructor called; number of objects: 4
    192 Number of elements: 0 Data address: 00000000
    193 three = one
    194 copy assignment operator= called;
    195 Now Object three = xxxxxxxxxx
    196 And Object one = xxxxxxxxxx
    197 four = one + two
    198 Entering operator+()
    199 Useless(int k) constructor called; number of objects: 5
    200 Number of elements: 30 Data address: 002049F0
    201 temp object:
    202 Leaving operator+()
    203 move constructor called; number of objects: 6
    204 Number of elements: 30 Data address: 002049F0
    205 destructor called; deleted object: Number of elements: 0 Data address: 00000000
    206 objects left: 5
    207 move assignment operator= called;
    208 destructor called; deleted object: Number of elements: 0 Data address: 00000000
    209 objects left: 4
    210 Now Object four = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    211 four = move(one)
    212 move assignment operator= called;
    213 Now Object four = xxxxxxxxxx
    214 And Object one = (object empty)
    215 destructor called; deleted object: Number of elements: 10 Data address: 00204910
    216 
    217 objects left: 3
    218 destructor called; deleted object: Number of elements: 10 Data address: 002049A8
    219 
    220 objects left: 2
    221 destructor called; deleted object: Number of elements: 20 Data address: 00204958
    222 
    223 objects left: 1
    224 destructor called; deleted object: Number of elements: 0 Data address: 00000000
    225 objects left: 0
    226 */

    运作结果分析如下:

    移动赋值运算符函数如下:

     1 Useless & Useless::operator=(Useless && f)
     2 {
     3     cout << "move assignment operator= called;
    ";
     4     if (this == &f)
     5         return *this;
     6     delete []pc;
     7     n = f.n;
     8     pc = f.pc;
     9     f.n = 0;
    10     f.pc = nullptr;
    11     return *this;
    12 }

    移动赋值运算符删除目标对象中的原始数据,并将源对象的所有权转让给目标对象。不能让多个指针指向相同的数据(同上移动构造函数的原理)。

    【4】强制移动

    移动构造函数和移动赋值运算符使用右值。如果想要强制移动,即要让它们使用左值作为实参,可使用运算符static_cast<>将对象的类型强制转换为Useless &&。

    但C++11提供了更简单的方式——使用头文件utility中声明的函数std::move()。如上示例中已使用。

    通过例子比较可知,函数std::move()并非一定会导致移动操作。表达式std::move(one)是右值,因此上例赋值语句将调用其移动赋值运算符(前提条件是定义了移动赋值运算符)。

    如果没有定义移动赋值运算符,编译器将使用复制赋值运算符。如果也没有定义复制赋值运算符,将根本不允许上例的赋值。

    Good Good Study, Day Day Up.

    顺序 选择 循环 总结

  • 相关阅读:
    Setting up SharePoint Blogs, Wikis, and Announcements
    Reports 将透视表显示成没有级联关系的普通表
    TF80012: The document cannot be opened because there is a problem with the installation of the Microsoft Visual Studio v
    项目干系人分析的“四步法”
    Overload和Override的区别
    Add and Customize a Type of Work Item
    将所有的非DLL, PDB文件从一个位置Copy到另外一个位置,相同文件跳过
    如何删除工作项
    No Excel Calculation Services is available in the farm
    SPORTINGLIFE.COM
  • 原文地址:https://www.cnblogs.com/Braveliu/p/6220906.html
Copyright © 2020-2023  润新知