先了解引用的作用:C++引用的作用_Lao_tan的博客-CSDN博客_c++引用的作用
右值引用_百度百科
C++中rvalue和lvalue详悉_Naruto_Q的博客-CSDN博客_lvalue rvalue
lvalue(left value)、rvalue(right value)是c语言编译过程中的用于描述等号左右值的符号。但是不能直观的通过在等号左边还是右边来判断是lvalue还是rvalue。能够取地址的,有名字的就是左值;反之,不能取地址,没有名字的就是右值。
左值(lvalue)可以是明确的变量。右值(rvalue)可以是临时变量,比如表达式、函数返回值、常量等。
int a = 1;
int b;//下面为左值引用
int &aa0 = a;//ture
int &bb0 = b;//ture
int &aa1 =1;//error
int &aa1 = a+1;//error//下面为右值引用
int &&a1 = a;//error
int &&b1 = b;//error
int &&a2 = 1;//ture
int &&a3 = a+1;//ture
int &&a4 = std::move(a);//ture ,std::move()强制将左值转换成右值
- //可以通过这个函数来判断是lvalue还是rvalue。
- bool is_r_value(int &&)
- {
- return true;
- }
- bool is_r_value(const int &)
- {
- return false;
- }
可以将C++的引用理解为指针常量。引用必须在声明时初始化,声明之外的赋值操作都是对引用所代指地址的内容访问。
C++参数中的& 和&& 都表示引用。左值的引用(lvalue)声明符号为”&”, 为了和左值区分,右值的引用(rvalue)声明符号为”&&”。 右值引用初始化之后引用本身变成了lvalue。所以无法进行“&&=&&”,从而达到右值引用传递的目的;但是左值引用可以传递,所以可以进行“&=&”。引用初始化必须满足lvalue = lvalue或者rvalue = rvalue。
不同编辑器表现可能会有差异。比如vs中对类或结构体操作时不一定非要满足lvalue = lvalue或者rvalue = rvalue的原则。
- static int t = 0;
- class Whore {
- public:
- char buf[10];
- Whore()
- {
- itoa(t++,buf,10);
- cout << "Whore():" << buf<< endl;
- }
- ~Whore()
- {
-
- cout << "~Whore():"<
- }
- }
-
- void fun(int &a)
- {
- cout << "fun(int &a)" << endl;
- }
-
- void fun(int &&a)//&&用于存放右值(可以是表达式、临时变量等等)的引用
- {
- cout << "fun(int &&a)" << endl;
- }
-
- void fun1(Whore &a)
- {
- cout << "fun1(Whore &a)" << endl;
- }
- void fun1(Whore &&a)
- {
- cout << "fun1(Whore &&a)" << endl;
- }
-
- void fun2(Whore &a)
- {
- cout << "fun2(Whore &a)" << endl;
- }
-
- void fun3(int &a)
- {
- cout << "fun3(int &a)" << endl;
- }
-
- int main()
- {
- int a = 0;
- Whore w;
- fun(a);
- fun(1); //1为匿名临时变量
- fun1(w);
- fun1(Whore());//Whore()为匿名临时变量,生命周期为fun1(Whore && a)的函数栈。
- //fun2(Whore()); //vs编辑器不报错,编译不报错,可执行;qt编辑器报错:(no matching function for call to 'fun2'),不可执行
- //fun3(1); //vs编辑器报错:(非常量引用的初始值必须为左值),编译报错:(无法将参数 1 从“int”转换为“int &”),不可执行;qt编辑器报错:(no matching function for call to 'fun3'),不可执行
- cout<<"end"<
-
- int &a0 = a;用变量对左值引用进行初始化,等价于lvalue = lvalue。
- a0 = 2;
- //int &a1 = 1;//vs编辑器报错:(非常量引用的初始值必须为左值),编译器报错:(无法从“int”转换为“int &”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &”)
- //int &a1 = a+1;//vs编辑器报错:(非常量引用的初始值必须为左值),编译器报错:(无法从“int”转换为“int &”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &”)
- int &a1 = a0;//用左值引用对左值引用进行初始化,等价于lvalue = lvalue。
- const int &a2 = 1;//该语句在qt和vs中的汇编语言等价于“int &&a2 = 1;”,但是该语句a2指向的地址的内容无法修改。
- int &&a3 = 1;//用常数对右值引用进行初始化,等价于rvalue = rvalue。
- a3 = 2;
- const int &&a4 = 1;
- //int &&a5 = a;//用变量对右值引用初始化,等价于rvalue = lvalue,失败。//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“int &&”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &&”)
- //int &&a6 = a0;//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“int &&”);qt编辑器报错:(rvalue reference to type 'int' cannot bind to lvalue of type 'int'),qt编译器报错:(无法从“int”转换为“int &&”)
- int &&a7 = a+0;//用表达式对右值引用进行初始化,等价于rvalue = rvalue。
- int &&a8 = a0+0;//用表达式对右值引用进行初始化,等价于rvalue = rvalue。
- //int &&a9 = a3;//用已经初始化的右值引用对右值引用初始化,然而引用初始化之后引用本身变成了lvalue,等式等价于rvalue = lvalue,所以无效。//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“int &&”);qt编辑器报错:(error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &&”)
- int &&a10 = a3+0;//用表达式对右值引用进行初始化,等价于rvalue = rvalue。
- //const int &&a11 = a0;//vs编辑器报错:(无法将右值引用绑定到左值),编译器报错:(无法从“int”转换为“const int &&”);qt编辑器报错:(error: non-const lvalue reference to type 'const int' cannot bind to a temporary of type 'int') ,qt编译器报错:(无法从“int”转换为“int &&”)
- int &a12 = a3;//用已经初始化的右值引用对左值引用初始化,相当于进行lvalue = lvalue操作。
- fun(a);
- fun(a0);
- fun(a3);
- //fun(a4);//vs编辑器报错:(没有与参数列表匹配的 重载函数 "fun" 实例),vs编译器报错:(“fun”: 2 个重载中没有一个可以转换所有参数类型);qt编辑器报错:( no matching function for call to 'fun'),编译器报错:(“fun”: 2 个重载中没有一个可以转换所有参数类型)
- fun(1+1);
- fun(a+1);
- fun(a0+0);
- fun(a3+1);
- return 0;
- }
-
- /*输出
- Whore():0
- fun(int &a)
- fun(int &&a)
- fun1(Whore &a)
- Whore():1
- fun1(Whore &&a)
- ~Whore():1
- end
- fun(int &a)
- fun(int &a)
- fun(int &a)
- fun(int &&a)
- fun(int &&a)
- fun(int &&a)
- fun(int &&a)
- ~Whore():0
- */
C++临时变量的生命周期_snail0428的博客-CSDN博客_临时变量生命周期
【C++】拷贝构造函数的调用时机_翼同学的博客-CSDN博客_c++拷贝构造函数什么时候调用
返回引用的意义_陈小淘的博客-CSDN博客_函数返回引用的含义
C++将返回值为引用有什么作用? - 知乎
(右值)引用可以改变临时变量的生命周期!!!函数返回类型绝对不能返回右值引用:T &&,返回右值引用再逻辑上不成立,且会造成非法越界访问。返回的左值引用也可能会造成越界访问,需要理解清楚左值引用所引用对象的生命周期。
- static int t = 0;
- class Whore {
- public:
- char nickname[10];
- Whore()
- {
- _itoa(t++,buf,10);
- cout << "Whore():"<< nickname << endl;
- }
- Whore(Whore & w)
- {
- char buf[10];
- nickname = _itoa(t++, buf, 10);
- cout << "Whore(Whore & w):" << nickname << endl;
- }
- ~Whore()
- {
- cout << "~Whore():"<
- }
- Whore& operator =(const Whore & w)
- {
- ///char buf[10];
- //nickname = _itoa(t++, buf, 10);
- cout << "Whore& operator =(const Whore & w):" << nickname << ":" << w.nickname << endl;
- strcpy(nickname , w.nickname);
- return *this;
- }
- Whore& operator =(Whore && w)
- {
- ///char buf[10];
- //nickname = _itoa(t++, buf, 10);
- cout << "Whore& operator =(Whore && w):" << nickname <<":"<
- strcpy(nickname , w.nickname);
- return *this;
- }
- }
- Whore fun4()
- {
- return Whore();
- }
- Whore fun4_1()
- {
- Whore w = Whore();
- return w;
- }
- Whore & fun5()//错误示范1,返回临时或局部变量的引用,在函数退出后临时变量会被回收,引用也会变的无效,fun5()之外再访问w的成员造成越界。
- {
- Whore w = Whore();
- return w;//临时或局部变量在函数调用完后会被释放,外部再使用会导致越界访问。
- }
- Whore && fun5_1()//错误示范2,返回临时或局部变量的引用,在函数退出后临时变量会被回收,引用也会变的无效,fun5_1()之外再访问w的成员造成越界。
- {
- return Whore();//临时或局部变量在函数调用完后会被释放,外部再使用会导致越界访问。
- }
- Whore & fun6()//错误示范3,左值引用无法改变变量生命周期
- {
- Whore wt = Whore();
- static Whore & w = wt;//左值引用无法改变局部变量生命周期。
- return w;
- }
- Whore & fun6_1()
- {
- static Whore & w = Whore();//在vs中能运行成功,在qt中编辑器报错。这里用临时变量初始化左值引用,相当于lvalue = rvalue,正常来讲应该是要报错的。在vs中,这里直接将该临时变量变成全局变量来使用了。
- return w;
- }
- Whore & fun6_2()
- {
- static Whore && w = Whore();//右值引用可以改变临时变量的生命周期。
- return w;
- }
- Whore fun6_3()
- {
- static Whore && w = Whore();//右值引用可以改变临时变量的生命周期。
- return w;
- }
- Whore fun7()
- {
- static Whore w = Whore();
- return w;
- }
- Whore& fun7_1()
- {
- static Whore w = Whore();
- return w;
- }
- void fun8(Whore & w)
- {
- cout << "void fun8(Whore & w)" << endl;
- }
- void fun8(Whore && w)
- {
- cout << "void fun8(Whore && w)" << endl;
- }
- int main()
- {
- /*
- //下面代码运行成功。注意fun4与fun4_1返回变量与返回临时变量的差异。
- Whore w1 = fun4();//debug 和release下结果不一样,debug中调用构造函数后再调用拷贝构造,release中只调用构造函数。
- cout<
- Whore w2;
- cout<
- w2 = fun4();//调用了赋值重载函数,此时临时变量的生命周期在这条语句结束时结束了。
- cout<
- Whore w3 = fun4_1();//只调用了一次构造函数,等价于Whore ww1 = Whore();此时临时变量的生命周期与ww1等价了。
- cout<
- Whore &w4 = fun4();
- cout<
- Whore &w5 = fun4_1();
- cout<
- Whore &&w6 = fun4();//右值引用拉长了临时变量的生存周期
- cout << w6.nickname << endl;
- Whore &&w7 = fun4_1();//右值引用拉长了临时变量的生存周期
- cout << w7.nickname << endl;
- *//*debug输出:
- Whore():0
- Whore(const Whore &w):1
- ~Whore():0
- 1
- Whore():2
- 2
- Whore():3
- Whore(const Whore &w):4//w2 = fun4();在debug中调用了拷贝构造之后再调用赋值重载函数函数。拷贝构造的作用应该是将fun4中的返回的临时变量先存放到main中一个临时变量值中,然后再main中的临时变量赋值给w2。这个过程设计的内存操作相当的多。在release下有很大的精简。
- ~Whore():3
- Whore& operator =(Whore && w):2:4
- ~Whore():4
- 4
- Whore():5
- 4
- Whore():6
- Whore(const Whore &w):7
- ~Whore():6
- 7
- Whore():8
- 8
- Whore():9
- Whore(const Whore &w):10
- ~Whore():9
- 10
- Whore():11
- 11
- end
- ~Whore():11
- ~Whore():10
- ~Whore():8
- ~Whore():7
- ~Whore():5
- ~Whore():4
- ~Whore():1
- *//*release 输出: //release 模式下少了很多拷贝构造函数的调用,临时变量在fun4退出后并没有被西沟,意味着都直接延长了临时变量生命周期。在release模式下,右值引用与普通变量基本没有差别
- Whore():0
- 0
- Whore():1
- 1
- Whore():2
- Whore& operator =(Whore && w):1:2
- ~Whore():2
- 2
- Whore():3
- 2
- Whore():4
- 4
- Whore():5
- 5
- Whore():6
- 6
- Whore():7
- 7
- end
- ~Whore():7
- ~Whore():6
- ~Whore():5
- ~Whore():4
- ~Whore():3
- ~Whore():2
- ~Whore():0
- */
- /*
- //下面代码在qt中失败,在vs中成功。按规则来讲,临时变量是不能赋值给左值引用的,但vs中对类或结构体操作时是可以的。
- Whore &w = fun4();
- cout << w.nickname << endl;
- */
- /*
- //下面的代码导致崩溃。fun5返回的是临时变量的引用,而该临时变量的生命周期只在“Whore &w = fun5();”语句之中,在main中通过引用再访问变量的内容时,变量已经被销毁了,所以再访问就是越界访问了。
- Whore &w = fun5();
- cout<
- Whore &w1 = fun5_1();
- cout<
- *//*release输出:
- Whore():0
- ~Whore():0//fun5中的临死变量在fun5退出后被析构了,与fun4 的区别在于一个是返回了临时变量或局部,一个是返回了临时或局部变量的引用。
- Whore(const Whore &w):1
- 1
- Whore():2
- ~Whore():2
- Whore(const Whore &w):3
- 3
- end
- ~Whore():3
- ~Whore():1
- *//*debug输出:
- Whore():0
- ~Whore():0
- Whore(const Whore &w):1
- 1
- Whore():2
- ~Whore():2
- Whore(const Whore &w):3
- 3
- end
- ~Whore():3
- ~Whore():1
- */
- /*
- //下面代码导致崩溃。fun6中定义了一个全局静态左值引用,并且用一个局部变量初始化左值引用,然而局部变量的生命周期只在fun6的函数栈内,在main中通过fun6返回的引用再访问变量的内容时,变量已经被销毁了,所以再访问就是越界访问了。
- Whore &w = fun6();
- cout << w.nickname << endl;
- Whore &w = fun6_1();
- cout<< w.nickname <
- */
- /*下面代码运行成功:展示了返回引用的一种正确用法,返回引用更多的是用在返回对象成员变量或传入参数的成员变量
- //fun6中用临时变量初始化右值引用,是合法的,临时变量生命周期也强制变长了。全局静态变量w初始化之后就成了lvalue了然后以引用的形式进行返回。
- Whore &w = fun6_2(); //整个过程只有一次在fun6_2中调用一次构造函数。返回引用的正确用法,人为避免调用拷贝构造函数
- cout << w.nickname << endl;
- *//*debug/release输出:
- Whore():0
- 0
- end
- ~Whore():0
- */
- /*下面代码运行成功:都是展示函数返回引用后的错误用法
- Whore w1 = fun6_2(); //函数虽然返回引用,但是依然调用拷贝构造函数。
- cout << w1.nickname << endl;
- //Whore &&w2 = fun6_2()//qt编辑器报错,不能将rvalue绑定到lvalue。
- //Whore &w3 = fun6_3(); //qt编辑器报错,不能将lvalue绑定到临时变量
- Whore w4 = fun6_3();//将fun6_3返回的右值引用赋值给普通变量,会调用拷贝构造函数。注意fun4的区别,fun4不会调用拷贝构造函数。
- cout << w4.nickname << endl;
- Whore && w5 = fun6_3(); //将fun6_3返回的右值引用赋值给右值引用,依然会调用拷贝构造函数。
- cout << w5.nickname << endl;
- *//*debug/release输出:
- Whore():0
- Whore(const Whore &w):1
- 1
- Whore():2
- Whore(const Whore &w):3
- 3
- Whore(const Whore &w):4
- 4
- end
- ~Whore():4
- ~Whore():3
- ~Whore():1
- ~Whore():2
- ~Whore():0
- */
- /*下面代码运行成功:正确用法
- Whore &w = fun7_1();//整个过程只有在fun7_1中一次调用了构造函数。人为避免调用拷贝构造函数
- cout<
- *//*debug/release 输出:
- Whore():0
- 0
- end
- ~Whore():0
- */
- /*下面代码运行成功:错误用法
- Whore wz = fun7();
- cout<
- Whore &&w1 = fun7();
- cout<
- Whore w2 = fun7_1();
- cout<
- *//*debug/release输出:
- Whore():0
- Whore(const Whore &w):1
- 1
- Whore(const Whore &w):2
- 2
- Whore():3
- Whore(const Whore &w):4
- 4
- end
- ~Whore():4
- ~Whore():2
- ~Whore():1
- ~Whore():3
- ~Whore():0
- */
- /*
- //下面代码运行成功。
- Whore w;
- fun8(w);
- fun8(Whore());
- *//*输出:
- Whore():0
- void fun8(Whore & w)
- Whore():1
- void fun8(Whore && w)
- ~Whore():1
- end
- ~Whore():0
- */
- cout <<"end"<
- return 0;
- }
-
相关阅读:
机器学习之分类
京东面试题:ElasticSearch深度分页解决方案
京东数据挖掘(京东数据采集):2023年Q3电脑行业数据分析报告
公共命名空间和输入法
高性能MySQL实战第03讲:深入理解事务与锁机制(下)
Scala系列-5、scala中的泛型、actor、akka
消除注释C++
图像分割 - 阈值处理 - 固定阈值法
JVM-查看服务器JVM垃圾收集器类型
Proteus下仿真AT89C51单片机串行口的问题
-
原文地址:https://blog.csdn.net/qiushangren/article/details/126053951