c++右值引用-学习笔记

仅为个人理解,个人笔记
部分代码和内容引用于文章从4行代码看右值引用
代码(来自链接文章里)

#include <iostream>
using namespace std;

int g_constructCount=0;
int g_copyConstructCount=0;
int g_destructCount=0;
struct A
{
    A(){
        cout<<"construct: "<<++g_constructCount<<endl;    
    }
    
    A(const A& a)
    {
        cout<<"copy construct: "<<++g_copyConstructCount <<endl;
    }
    ~A()
    {
        cout<<"destruct: "<<++g_destructCount<<endl;
    }
};

A GetA()
{
    return A();
}

int main() {
    A a = GetA();
    return 0;
}

加上-fno-elide-constructors 输出结果
construct: 1
copy construct: 1
destruct: 1
copy construct: 2
destruct: 2
destruct: 3
解释
第一次构造是在GetA()函数栈里调用A()而生成的,此时对象保存在GetA()函数栈里
第一次拷贝构造是在GetA()函数栈里完成的,目的是为了将对象返回到main()函数栈里,从GetA()函数返回到main()函数栈里的对象称为<临时对象>
第一次析构是在GetA()函数栈里完成的,是在GetA()函数返回之前的最后一步操作
然后,恢复main()函数栈的上下文,并且将从GetA()函数返回的值也放在main()函数栈中
接下来第二次拷贝构造是将main()函数栈里从GetA()函数返回的对象拷贝到变量a中,此时变量a是一个对象,且在main()函数栈中
第二次析构函数是将main()函数栈里从GetA()函数返回的对象出栈,此时<临时对象>的生命结束
接下来是main函数结束
第三次析构函数是因为main函数结束,且a对象不是返回值,因此不用调用拷贝构造函数生成main函数的临时返回值对象

采用右值引用

代码(来自链接文章里)

int main() {
    A&& a = GetA();
    return 0;
}

加上-fno-elide-constructors 输出结果
construct: 1
copy construct: 1
destruct: 1
destruct: 2
解释
因为在main函数里采用了右值引用,此时a变量不再是一个对象,而是一个引用(引用本质上是一个指针常量),且a存放的地址是临时对象的地址,且临时对象不会出栈,因此临时对象也不会析构,在main函数结束时才会统一出栈,因此a变量和临时变量的生命周期一样,是main()函数栈的生命周期
采用右值引用后相对没有采用右值引用,提高了时间效率(取消了临时对象的拷贝和析构),但在mian()函数栈里要多消耗一小点空间,这个空间根据系统不同而不同,例如32位系统就会在main()函数栈里多消耗32bit

移动构造函数与右值

移动构造函数举例

    classname(classname&& rightVlue){
	...
	}

只有在赋值语句(如=号)右边的值是右值时才会使用这个移动构造函数,例如函数的返回值,或者应用移动语义std::move将左值对象转换成一个右值对象
作用:因为拷贝构造函数一定要进行深拷贝(避免类内的指针漂移),因此在对临时对象进行拷贝时也要进行深拷贝,这样增加了不必要的开销,因为调用拷贝构造函数进行深拷贝是为了保护被拷贝的对象,但是临时对象在拷贝构造函数调用完后,就直接析构了(如果不使用右值引用的话),所以直接写一个移动构造函数,当要拷贝的对象是临时对象(右值)的时候,直接移动对象而不是去拷贝对象(例如临时对象成员p指向一块堆内存,那么移动构造函数不是再像拷贝构函数那样malloc一块堆内存被拷贝,而是直接继承临时对象的这一块堆内存,然后将临时对象的p置空,这样在析构时也会不删除这块堆内存),这样就减小了不必要的开销

移动语义std::move

作用:将一个对象转换成右值(不管对象原来是左值还是右值)
直接上代码

template<typename T>
void print(T&& t){
    cout << "rvalue" << endl;
}
template<typename T>
void print(T& t){
    cout << "lvalue" << endl;
}
int main(){
	int x = 1 ;
	print(std::move(1));
	print(std::move(x));
}

输出:
rvalue
rvalue

完美转发std::forward

作用 : 用在模板函数中,可以按照参数原来的类型进行转发
不使用完美转发

template<typename T>
void print(T&& t){
    cout << "rvalue" << endl;
}
template<typename T>
void print(T& t){
    cout << "lvalue" << endl;
}
template <typename T>
void foo(T&& x){
	print(x);
}
int main(){
	int x = 1 ;
	foo(1);
	foo(x);
} 

输出 :
lvalue
lvalue
使用完美转发

template<typename T>
void print(T&& t){
    cout << "rvalue" << endl;
}
template<typename T>
void print(T& t){
    cout << "lvalue" << endl;
}
template <typename T>
void foo(T&& x){
	print(std::forward<T>(x));
}
int main(){
	int x = 1 ;
	foo(1);
	foo(x);
} 

输出
rvalue
lvalue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值