c/c++语言开发共享c++ 右值引用

1. 左值和右值 简单的定义来说,能够放在赋值等号左边的就是左值,反之则是右值(所有表达式不是左值就是右值,左右值不存在交集)——但是这个解释实在有点鸡肋。下面对定义结合例子做些补充。 右值:在内存中不占有内存的表达式 左值:在内存中占有一定内存位置的表达式 例子合法性很好理解——可以在结合左右值的 …


1. 左值和右值

  简单的定义来说,能够放在赋值等号左边的就是左值,反之则是右值(所有表达式不是左值就是右值,左右值不存在交集)——但是这个解释实在有点鸡肋。下面对定义结合例子做些补充。

  • 右值:在内存中不占有内存的表达式
  • 左值:在内存中占有一定内存位置的表达式
1 int i;  2 i = 2; //合法  3 2 = i; //非法

  例子合法性很好理解——可以在结合左右值的定义。

 1 #include<iostream>   2 int main()   3 {   4     int t;   5     int* q = &(t + 1);//非法,t+1在内存中没有位置   6    7     int arr[] = { 1,2,3 }; int r[] = { 1,2,3 };   8     int* i = arr; //合法   9     std::cout << arr << std::endl; // 006ff808  10     arr = r; // 非法  11     *(arr) = 10;//合法  12 }

  在这里,我们需要理解下第八行,arr 作为右值—— 注意 arr 是内存地址,是一个地址,不同于第一个例子中的 i 。在第九行可以看到打印的 arr 代表的地址内容,第十行也就必定是错误的(是右值就不可能作为左值)。

  而第11行,arr 貌似变成了左值——也就是左值和右值在一定程度上是可以转换的。其中扮演关键角色的就是 * 解引用。既然是左值,按照我们前面说的,那应该在内存中存在一处分配的内存位置,*arr 解引用之后的确是存了 1 这个数,所以可以重新赋值10。

 

2. 右值引用

  通过下面的例子来理解:

 1 #include<iostream>   2 #include<string>   3 using namespace std;   4 struct a   5 {   6     // 构造函数   7     explicit a(size_t _size = 0) :size(_size),ptr(new int[_size]()) {   8         cout << "construct..."<< endl;   9     }  10   11     // 拷贝赋值运算符  12     a& operator=(const a& tmp) {  13         if (&tmp != this)  14         {  15             this->ptr = new int[tmp.size];  16             for (size_t i = 0; i < tmp.size; ++i) {  17                 this->ptr[i] = tmp.ptr[i];  18             }  19         }  20         cout << "copy assignment 等于..." << endl;  21         return *this;  22     }  23   24     // 拷贝构造函数  25      a(const a& tmp) {  26         this->ptr = new int[tmp.size];  27         for (size_t i = 0; i < tmp.size; ++i) {  28             this->ptr[i] = tmp.ptr[i];  29         }  30         cout << "copy construct..." << endl;  31     }  32   33     // 析构函数  34     ~a() {  35         if (ptr)   36         {  37             delete[] ptr;  38             size = 0;  39             cout << "destructor..." << endl;  40         }  41     }  42   43 private:  44     int* ptr;  45     size_t size;  46 };  47   48 int main()  49 {  50     a a(10);  51     a b;  52   53     cout<< "=====start=====" <<endl;  54     b = a;  55     cout << "=====end=====" << endl << endl;  56   57     cout << "=====start2=====" << endl;  58     a c = a;//是拷贝构造。或者加上explicit改成a c(a) 可能更好理解  59     cout << "=====end2=====" << endl << endl;  60   61     a d;  62     cout << "=====start3=====" << endl;  63     d = a(5);  64     cout << "=====end3=====" << endl << endl;  65   66     return 0;  67 }

c++ 右值引用

 

 

   结果上看,应该都比较好理解。

  其中第三处是要引出的重点,可以看到应该使用了 d = a(10),这条语句,a() 是一个临时变量,在完成 construct -> copy assignment 之后就进行了 destructor。我们来讨论下这一句赋值语句的内部过程,a(10) 调用构造函数并申请了内存空间,然后去实现拷贝赋值运算符,临时变量a(10)的内容【copy】给 d 变量,随后 a(10) 被销毁。而我们知道在 copy assignment (拷贝赋值运算符)方法中,我们又为 d 变量同样申请了 大小为10的数组空间,这就出现了一定的“低效处理”。不妨这样想,如果我们将a(10)申请的空间,通过拷贝赋值运算符,直接将这个空间给变量d,这样d就不用再去申请空间(反正a(10)的空间申请了,马上就会释放。相当于白白多申请和释放了一次,不如直接将a(10)的空间转移到d变量名下)。

  这就引入了右值引用,如下例子:

 1 #include<iostream>   2 #include<string>   3 using namespace std;   4 struct a   5 {   6     // 构造函数   7     explicit a(size_t _size = 0) :size(_size),ptr(new int[_size]()) {   8         cout << "construct..."<< endl;   9     }  10   11     // 拷贝赋值运算符  12     a& operator=(const a& tmp) {  13         if (&tmp != this)  14         {  15             this->ptr = new int[tmp.size];  16             for (size_t i = 0; i < tmp.size; ++i) {  17                 this->ptr[i] = tmp.ptr[i];  18             }  19         }  20         cout << "copy assignment 等于..." << endl;  21         return *this;  22     }  23   24     // 使用右值引用的拷贝赋值运算符  25     a& operator=(a&& tmp) {  26         if (&tmp != this)  27         {  28             this->ptr = tmp.ptr;  29             this->size = tmp.size;  30   31             tmp.ptr = nullptr;  32             tmp.size = 0;  33         }  34         cout << "copy assignment 等于2..." << endl;  35         return *this;  36     }  37   38     // 拷贝构造函数  39      a(const a& tmp) {  40         this->ptr = new int[tmp.size];  41         for (size_t i = 0; i < tmp.size; ++i) {  42             this->ptr[i] = tmp.ptr[i];  43         }  44         cout << "copy construct..." << endl;  45     }  46   47     // 析构函数  48     ~a() {  49         if (ptr)   50         {  51             delete[] ptr;  52             size = 0;  53             cout << "destructor..." << endl;  54         }  55     }  56   57 private:  58     int* ptr;  59     size_t size;  60 };  61   62 int main()  63 {  64     a a(10);  65     a b;  66   67     cout<< "=====start=====" <<endl;  68     b = a;  69     cout << "=====end=====" << endl << endl;  70   71     cout << "=====start2=====" << endl;  72     a c = a;//是拷贝构造。或者加上explicit改成a c(a) 可能更好理解  73     cout << "=====end2=====" << endl << endl;  74   75     a d;  76     cout << "=====start3=====" << endl;  77     d = a(10);  78     cout << "=====end3=====" << endl << endl;  79   80     return 0;  81 }

c++ 右值引用

   可以看到通过使用右值引用,执行一样的 d = a(5)操作会调用右值引用的拷贝运算符方法。这样写就能达到如下效果:a(5)申请了临时空间,但是这空间通过拷贝运算符方法(右值引用)直接被 d 变量接管,d 变量无需再去申请空间了注意:第31,32行代码不能不写,经过28,29行之后,d 变量和 a(5) 临时变量的指针都指向了同一块内存,而之后 a(5) 将被销毁,所以需要将 a(5) 的指针置为 nullptr,但这部分内存由 d 对象接管。注意:这里看上去好像少了临时变量 a(5) destructor(没有打印),其实对象 a(5) 也是执行了析构,析构时释放这个类——也就是临时a(5)这个类,但是因为我析构函数有个if条件,所以只是看上去没有打印罢了,反正依旧会执行析构。

 

3. std::move()

 

  

 

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/601639.html

(0)
上一篇 2021年5月10日
下一篇 2021年5月10日

精彩推荐