C++移动和拷贝

今天面试遇到这么个问题:
有如下代码:

vector<int>f(){}
vector<int>ret=f();

假设函数f返回的vector很大,我们不想拷贝,但又想得到函数f返回的值怎么办?
我当时想到了移动,但是不清楚怎么用。
其实这里的答案很简单,vector<int>ret=f();这句话本身就会调用vector的移动构造函数,不会发生拷贝。
为了弄清楚为什么,我做了以下实验

class A
{
public:
	A(){}
	//拷贝构造
	A(const A& rhs) {
		cout << "copy construct" << endl;
	}
	//移动构造
	A(A &&a) {
		cout << "move construct" << endl;
	}
	//拷贝赋值
	A& operator=(const A &rhs) {
		cout << "copy assign" << endl;
		return *this;
	}
	//移动赋值
	A& operator=(A &&rhs) {
		cout << "move assign" << endl;
		return *this;
	}
};

A testa() {
	A a;
	return a;
}
int main()
{
	A a1, a2;
	a1=testa();
	a2 = a1;
}

先放输出
在这里插入图片描述

移动构造发生在函数testa返回a的时候,使用移动构造函数创建一个临时对象。
然后再调用移动赋值运算符,把该临时对象赋值给a1。
这里有一个疑惑点,a是变量,变量是左值,难道不应该是调用拷贝构造函数构造返回的临时对象吗?
这点我没有在书上找到明确的答案,但是我推测原因可能是:a变量在执行完return语句之后,出了自身的作用域,即将被销毁,所以这时候把它的内容移动到返回的临时对象上是安全的,编译器基于此,做出这样的优化。
那么为什么a1=testa()这里使用移动赋值而不是拷贝赋值呢?
原因在于testa()是个临时对象,也就是右值,自然精确匹配到移动赋值运算符的参数列表A &&rhs
拷贝赋值发生在a2=a1
因为a1是左值,自然匹配到拷贝赋值运算符的const A &rhs。如果想让a1移动到a2而不是拷贝过去的话,需要改成这样:

a2=std::move(a1);

std::move得到a1的右值引用,这样就能精确匹配到类A的移动赋值运算符。
测试一下输出:
在这里插入图片描述
果然,使用了移动赋值。

如果把代码改成下面的:


int main()
{
	A a1=testa();
}

输出结果是:
在这里插入图片描述
这里只调用了一次移动构造函数。发生在return a的时候,直接把a移动到了a1。 而不是先移动到要返回的临时对象,然后再移动到a1。这里我也把它看成是编译器的优化。

此外,如果把移动构造和移动赋值注释掉,那么就只会调用拷贝构造和拷贝赋值了。原因如C++ primer中所说:

只有当一个类没有定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可以移动时,编译器才会为它合成移动构造函数或移动赋值运算符。

按这个说法,如果把类A中所有拷贝控制成员都注释掉,编译器会为我们合成移动构造函数或移动赋值运算符。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值