c++新特性实验(4)声明与定义:右值引用(C++11)

1.作用

  c++11以前,临时对象、字面常量一般情况下不可以再次访问,也不可以修改。右值引用可以解决这个问题。

1.1 实验A

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class A{
 5     int     id;
 6 public:
 7     A(int i) : id(i){
 8         cout << "A constructor " << id << endl;
 9     }
10     ~A(){
11         cout << "A destructor " << id << endl;
12     }
13     void fun(){
14         cout << "I'm " << id << endl;
15     }
16 };
17 
18 int 
19 main(int argc,char *argv[]){
20     
21     char ch = 'a';
22     {
23         A(1).fun();
24         cout << "exit {} " << endl;
25     }
26 
27     return 0;
28 }
复制代码

  结果:

A constructor 1
I'm 1
A destructor 1
exit {} 

  问题:

  1. 代码第21行和第23行的红色部分,一个是字面常量,一个产生A的临时对象。它们的生命期都是一行。
  2. 如果后续代码还想访问它们怎么办?
  3. 如果想修改A(1)产生的临时对象的值怎么办?
  4. 如果后面还有很多类似A(1).fun()这样的调用,每次都要分配空间,调用构造、析构。如果对象复杂点,那么就很费时。
  5. 临时对象生命期结束后,为什么还要去访问、修改它?不频繁的A(1)这样的调用,不就没有问题4了吗?

1.2 C++11以前解决问题2

  用const引用临时对象或者字面量,修改如下。

复制代码
 1 int 
 2 main(int argc,char *argv[]){
 3     
 4     {
 5         char ch = 'a';
 6         A(1).fun();
 7         cout << "exit {} " << endl;
 8     }
 9 
10     {
11         const char ch = 'b';
12         //A &ref = A(2);    //error,非const左值引用不能引用右值。cannot bind non-const lvalue reference of type ‘A&’ to an rvalue of type ‘A’
13         const A &ref = A(2);
14         ref.fun();
15         cout << "exit {} " << endl;
16     }
17 
18     return 0;
19 }
复制代码

  同时class A的fun()也要提供const版本。

1     void fun() const {
2         cout << "I'm " << id << endl;
3     }

1.3 c++11以前解决问题3、4

  无法解决问题3、4

  c++11起,可以用右值引用解决这两个问题,可以修改临时对象的值,也避免重复分配空间。

 

2.特性(C++11起)

  1. 它是右值的引用,右值的别名。
  2. 用两个“&&”声明
  3. 右值引用只能引用右值(字面常量,临时对象)。
  4. 右值引用声明时要初始化,成员右值引用要在构造初始化列表中初始化。
  5. 右值引用被列入函数重载规则
  6. 它引用的对象在释放前,可以通过它访问、修改(const 右值引用除外)。
  7. 它与引用占的空间一样。

2.实验B:特性[1-3]

2.1 左值引用字面量

1     char ch = 'a';
2     //char &lr = 'e';           //error,非const左值引用不能引用右值
3     const char &clr = 'b';      //ok

2.2 右值引用字面量

复制代码
 1     char ch = 'b';
 2     //char && rr2 = ch;                     //error,ch不是右值。
 3     char && rr1 = 'b';                      //ok
 4     char && rr2 = 'b' + getInt2();          //ok,普通函数
 5     char && rr3 = 'b' + 3 * 10 - 28 ^ 1;    //ok
 6     char && rr4 = 'b' + getInt() / 4;       //ok,constexpr函数
 7     //char && rr5 = rr4;                    //error,虽然rr4是合法的右值引用,但是它不是右值。
 8 
 9     char *pch = &ch;
10     char *&& prch1 = &rr4;                  //ok.
11     //char *&& prch2 = pch;                 //error,
12     prch1 = &ch;                            //ok.&ch得到ch的地址,它是右值。
13     
14     cout << " rr1 = " << rr1 << " rr2 = " << rr2 << " rr3 = " << rr3 << " rr4 = " << rr4 << " prch1 = " << *prch1 << endl; 
复制代码

  结果

   rr1 = b rr2 = l rr3 = e rr4 = i prch1 = b

2.3 右值引用临时对象

复制代码
 1     
 2     A a(0);
 3     //A &&rr1 = a;                          //error,a不是临时对象
 4     A(5).fun();
 5     A && rr2 = {3};
 6     A &r1 = rr2;                            //ok
 7     A *pa = &rr2;                           //ok
 8 
 9     rr2.fun();
10     r1.fun();
11     pa->fun();
12 
13     cout << "exit object test {}" << endl;
复制代码

  A的定义如下:

复制代码
 1 class A{
 2 public:
 3   void fun() const {
 4         cout << "I'm " << id << endl;
 5     }
 6 public:
 7     int     id;
 8     A(int i) : id(i){
 9         cout << "A constructor " << id << endl;
10     }
11     virtual ~A(){
12         cout << "A destructor " << id << endl;
13     }
14     A(const A &a){
15         cout << "A copy constructor ,from " << a.id << endl;
16         if(&a == this ) return;
17         this->id = a.id;
18     }
19     A& operator=(const A &o){
20         cout << "A operator= ,from " << o.id << endl;
21         if(&o == this ) return *this;
22         this->id = o.id;
23         return *this;
24     }
25     friend ostream& operator<<(ostream &os,const A &a);
26 };
复制代码

  运行结果:

复制代码
A constructor 0
A constructor 5
I'm 5
A destructor 5
A constructor 3
I'm 3
I'm 3
I'm 3
exit object test {}
A destructor 3
A destructor 0
复制代码

3.实验C:右值引用的初始化

  右值引用声明时要初始化,成员右值引用要在构造初始化列表中初始化。

1     //声明时要初始化
2     char && rr1 ;   //error,未初始化。

  成员右值引用的初始化。

复制代码
 1     class B{
 2     public:
 3         char &&rr;
 4         B():rr('d'){
 5         }
 6         B(const B &b):rr('d'){
 7         }
 8         B& operator=(const B &b){
 9             this->rr = b.rr;
10             return *this;
11         }
12         /* error
13         char&& getRR(){
14             return rr;
15         }
16         */
17     };
18     B b ;
19     cout << "b.rr = " << b.rr << endl;
20     
复制代码

  运行结果

  b.rr = d

4.实验D:右值引用与函数

4.1 右值引用被列入重载参考

复制代码
 1 class D { };
 2 
 3 void 
 4 foo(const D &){
 5     cout << " foo & called " << endl;
 6 }
 7 void 
 8 foo(const D &&){
 9     cout << " foo&& called " << endl;
10 }
复制代码

  测试代码

1     D d;
2     foo(d);
3     foo(D());

  结果:

   foo & called 
   foo&& called 

  如果把右值引用重载版本去掉,两个都输出:foo & called

4.2 右值引用与返回值

Rvalue references
Rvalue references can be used to extend the lifetimes of temporary objects (note, 
lvalue references to const can extend the lifetimes of temporary objects too,but they are not modifiable through them):

  右值引用可以用来为临时对象延长生存期。

  那它可以延长多久?能超出{}吗?能像堆上的对象的生命期一样?可以把函数返回值声明右值引用然后返回临时对象?

函数结束后,临时对象空间被释放,运行时会出错。

1 A&&
2 fun(){
3     cout << __func__ << endl;
4     return A(9);
5 }

  那如果返回全局的右值引用或者成员函数返回成员右值引用?

1 char && rrrr = 'd';
2 char &&
3 fch(){
4     return rrrr;
5 }

  或者

复制代码
1  class B{
2     public:
3         char &&rr;
4         // error
5         char&& getRR(){
6             return rr;
7         }
8         
9     };
复制代码

  都不可以。

  只能在它所在的 { } 延长一点生存期、出 } 就释放。

5.实验E:用右值引用修改临时对象

复制代码
 1     A && rr2 = {3};
 2     const A &&rr3 = A(4);                   //ok
 3     A &r1 = rr2;                            //ok
 4     A *pa = &rr2;                           //ok
 5 
 6     rr2.fun();
 7     r1.fun();
 8     pa->fun();
 9 
10     rr2.id = 99;
11 
12     rr2.fun();
13     r1.fun();
14     pa->fun();
15 
16     char && ch = 'd';
17     cout << "ch = " << ch << endl;
18     ch = 'e';
19     cout << "ch = " << ch << endl;
20     
21     const char && cch = 'f';
22     //cch = 'g';                            //error,const的
复制代码

6.实验F:右值引用占内存空间吗

6.1 成员引用占空间

在linux 64位、gcc7.4.0 下测试,类里的引用成员与一个指针占空间大小一样。

复制代码
 1     class F{
 2     };
 3     class E{
 4     public:
 5         char && rr;
 6         E():rr('e'){
 7         }
 8     };
 9     class G{
10     public:
11         char *pch;
12     };
13     class H{
14     public:
15         char &rch;
16         H(char ch):rch(ch){
17         }
18     };
19     
20     cout << "alignof(F) = " << alignof(F) << "\tsizeof(F) = " << sizeof(F) << endl;
21     cout << "alignof(E) = " << alignof(E) << "\tsizeof(E) = " << sizeof(E) << endl;
22     cout << "alignof(G) = " << alignof(G) << "\tsizeof(G) = " << sizeof(G) << endl;
23     cout << "alignof(H) = " << alignof(H) << "\tsizeof(H) = " << sizeof(H) << endl;
复制代码

  结果

alignof(F) = 1    sizeof(F) = 1
alignof(E) = 8    sizeof(E) = 8
alignof(G) = 8    sizeof(G) = 8
alignof(H) = 8    sizeof(H) = 8

  win10_64 + vs2019 结果:

alignof(F) = 1  sizeof(F) = 1
alignof(E) = 4  sizeof(E) = 4
alignof(G) = 4  sizeof(G) = 4
alignof(H) = 4  sizeof(H) = 4

6.2 验证G:引用是指针?

  如果引用是const指针,那么它应该有独立的地址。

1     int a = 10;
2     int &ra = a;
3     const int * const pa = &a;
4     
5     cout << " a.addr = " << &a  << endl;
6     cout << "ra.addr = " << &ra << endl;
7     cout << "pa.addr = " << &pa << endl;
8     cout <<"(*pa).addr = " << pa << endl;

  结果

   a.addr = 0x7fff3810df64
  ra.addr = 0x7fff3810df64
  pa.addr = 0x7fff3810df68
  (*pa).addr = 0x7fff3810df64

  如果ra是个类似pa一样的const指针,它应该和pa一样,有自己的地址,但是对ra取地址时,它和a的地址一样。普通引用不占空间。 

7.完整示例

  

复制代码
  1 #include <iostream>
  2 using namespace std;
  3 
  4 class A{
  5 public:
  6   void fun() const {
  7         cout << "I'm " << id << endl;
  8     }
  9 public:
 10     int     id;
 11     A(int i) : id(i){
 12         cout << "A constructor " << id << endl;
 13     }
 14     virtual ~A(){
 15         cout << "A destructor " << id << endl;
 16     }
 17     A(const A &a){
 18         cout << "A copy constructor ,from " << a.id << endl;
 19         if(&a == this ) return;
 20         this->id = a.id;
 21     }
 22     A& operator=(const A &o){
 23         cout << "A operator= ,from " << o.id << endl;
 24         if(&o == this ) return *this;
 25         this->id = o.id;
 26         return *this;
 27     }
 28     friend ostream& operator<<(ostream &os,const A &a);
 29 };
 30 ostream& operator<<(ostream &os,const A &a){
 31     os << "A.id = " << a.id << endl;
 32     return os;
 33 }
 34 
 35 constexpr int
 36 getInt(){
 37     return 30;
 38 }
 39 int getInt2(){
 40     int a = 10;
 41     return a;
 42 }
 43 
 44 class D { };
 45 
 46 void 
 47 foo(const D &){
 48     cout << " foo & called " << endl;
 49 }
 50 void 
 51 foo(const D &&){
 52     cout << " foo&& called " << endl;
 53 }
 54 
 55 
 56 A&&
 57 fun(){
 58     cout << __func__ << endl;
 59     return A(9);
 60 }
 61 
 62 /*
 63 char && rrrr = 'd';
 64 char &&
 65 fch(){
 66     return rrrr;
 67 }
 68 */
 69 int 
 70 main(int argc,char *argv[]){
 71     
 72     {
 73         char ch = 'a';
 74         //char &lr = 'e';                       //error,非const左值引用不能引用右值
 75         const char &clr = 'b';                  //ok
 76     }
 77     {
 78         char ch = 'b';
 79         //char && rr2 = ch;                     //error,ch不是右值。
 80         char && rr1 = 'b';                      //ok
 81         char && rr2 = 'b' + getInt2();          //ok,普通函数
 82         char && rr3 = 'b' + 3 * 10 - 28 ^ 1;    //ok
 83         char && rr4 = 'b' + getInt() / 4;       //ok,constexpr函数
 84         //char && rr5 = rr4;                    //error,虽然rr4是合法的右值引用,但是它不是右值。
 85 
 86         char *pch = &ch;
 87         char *&& prch1 = &rr4;                  //ok.
 88         //char *&& prch2 = pch;                 //error,
 89         prch1 = &ch;                            //ok.&ch得到ch的地址,它是右值。
 90         
 91         cout << " rr1 = " << rr1 << " rr2 = " << rr2 << " rr3 = " << rr3 << " rr4 = " << rr4 << " prch1 = " << *prch1 << endl; 
 92     }
 93     {
 94         A a(0);
 95         //A &&rr1 = a;                          //error,a不是临时对象
 96         A(5).fun();
 97         A && rr2 = {3};                         //ok
 98         const A &&rr3 = A(4);                   //ok
 99         A &r1 = rr2;                            //ok
100         A *pa = &rr2;                           //ok
101 
102         rr2.fun();
103         r1.fun();
104         pa->fun();
105 
106         cout << "exit object test {}" << endl;
107     }
108     {
109         //声明时要初始化
110         //char && rr1 ;                         //error,未初始化。
111         
112         class B{
113         public:
114             char &&rr;
115             /* error
116             char&& getRR(){
117                 return rr;
118             }
119             */
120             B():rr('d'){
121             }
122             B(const B &b):rr('d'){
123             }
124             B& operator=(const B &b){
125                 this->rr = b.rr;
126                 return *this;
127             }
128            
129             /* error
130             char&& getRR2(){
131                 return rrrr;
132             }
133             */
134         };
135         B b ;
136         cout << "b.rr = " << b.rr << endl;
137         
138     }
139     {
140         D d;
141         foo(d);
142         foo(D());
143 
144 
145         A && rr = fun();
146         //cout << " rr = " << rr << endl;
147     }
148 
149     {
150         A && rr2 = {3};
151         const A &&rr3 = A(4);                   //ok
152         A &r1 = rr2;                            //ok
153         A *pa = &rr2;                           //ok
154 
155         rr2.fun();
156         r1.fun();
157         pa->fun();
158 
159         rr2.id = 99;
160 
161         rr2.fun();
162         r1.fun();
163         pa->fun();
164 
165         pa->id = 3;
166 
167         rr2.fun();
168         r1.fun();
169         pa->fun();
170 
171 
172         char && ch = 'd';
173         cout << "ch = " << ch << endl;
174         ch = 'e';
175         cout << "ch = " << ch << endl;
176         
177         const char && cch = 'f';
178         //cch = 'g';                            //error,const的
179     }
180 
181 
182     {
183         class F{
184         };
185         class E{
186         public:
187             char && rr;
188             E():rr('e'){
189             }
190         };
191         class G{
192         public:
193             char *pch;
194         };
195         class H{
196         public:
197             char &rch;
198             H(char ch):rch(ch){
199             }
200         };
201         
202         cout << "alignof(F) = " << alignof(F) << "\tsizeof(F) = " << sizeof(F) << endl;
203         cout << "alignof(E) = " << alignof(E) << "\tsizeof(E) = " << sizeof(E) << endl;
204         cout << "alignof(G) = " << alignof(G) << "\tsizeof(G) = " << sizeof(G) << endl;
205         cout << "alignof(H) = " << alignof(H) << "\tsizeof(H) = " << sizeof(H) << endl;
206     }
207 
208     {
209         int a = 10;
210         int &ra = a;
211         const int * const pa = &a;
212         
213         cout << " a.addr = " << &a  << endl;
214         cout << "ra.addr = " << &ra << endl;
215         cout << "pa.addr = " << &pa << endl;
216         cout <<"(*pa).addr = " << pa << endl;
217 
218     }
219     return 0;
220 }
复制代码

 

posted @   f9q  阅读(710)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示