C++的std::move及相关概念

1 相关概念

定义 ,需要理解三个方面的概念才有可能完全掌握。
std::move : https://en.cppreference.com/w/cpp/utility/move
static cast : https://en.cppreference.com/w/cpp/language/static_cast
value_category:https://en.cppreference.com/w/cpp/language/value_category

表达式的值类型。
每个C++表达式(带操作数的运算符、文字、变量名等)都有两个独立的属性:类型和值类别。每个表达式都有一些非引用类型,并且每个表达式恰好属于三个主要值类别之一:prvalue、xvalue和lvalue。

  1. lvalue(左值):可以取到地址的值。注意“string”字符串是左值。
  2. prvalue(pure rvalue, 纯右值):不会在内存空间中出现,会出现在寄存器中,如a++,1,true。
  3. xvalue(eXpiring value, 将亡值): 会在内存空间中出现的临时值,会马上被销毁。
  4. glvalue: A glvalue expression is either lvalue or xvalue.
  5. rvalue: An rvalue expression is either prvalue or xvalue.

std::move的实现是将类型static_cast为右值引用。
std::move is used to indicate that an object t may be “moved from”, i.e. allowing the efficient transfer of resources from t to another object.
In particular, std::move produces an xvalue expression that identifies its argument t. It is exactly equivalent to a static_cast to an rvalue reference type.

https://mp.weixin.qq.com/s/_9-0iNUw6KHTF3a-vSMCmg
https://zhuanlan.zhihu.com/p/374392832

2 原理

理解std::move核心在于理解static_cast .
再往下一层在于理解右值引用和移动构造函数。

核心在于使用右值引用并实现移动构造函数/移动赋值函数,实现成员变量的数据的所有权的转移。

(1)static_cast的语法如下:
static_cast < new-type > ( expression )
(2)static_cast右值引用后会得到xvalue(这个不重要)
If new-type is an rvalue reference type, static_cast converts the value of glvalue, class prvalue, or array prvalue (until C++17)any lvalue (since C++17) expression to xvalue referring to the same object as the expression, or to its base sub-object (depending on new-type). If the target type is an inaccessible or ambiguous base of the type of the expression, the program is ill-formed. If the expression is a bit-field lvalue, it is first converted to prvalue of the underlying type. This type of static_cast is used to implement move semantics in std::move.
(3)xvalue是资源可重用对象(这个不重要)
an xvalue (an “eXpiring” value) is a glvalue that denotes an object whose resources can be reused;
a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);
(4)xvalue是rvalue,可以通过初始化const引用和右值引用延长生命周期(这个不重要)
An rvalue may be used to initialize an rvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.

(5) http://thbecker.net/articles/rvalue_references/section_01.html

每一类值对应不同的构造方法:
prvalue初始化glvalue,是直接构建,不用调用拷贝或移动构造函数;
xvalue初始化lvalue,会调用移动函数,也就是move;
lvalue初始化lvalue,会调用拷贝构造函数。

它们三者与move的关系是:
xvalue可以被直接move;
prvalue被move的时候,可以理解生成了一个临时变量xvalue,这个临时变量xvalue被move了;
如果move lvalue,那么需要使用std::move将lvalue变成xvalue,从而move生成后的xvalue。

c++的move全靠程序员自己根据语言提供的特性来实现,比如字符串类里面有个c str,你得自己实现移动构造函数,还得自己处理原字符串的状态,防止它在之后调用析构函数的时候破坏新字符串

3 代码测试

如果数据成员中没有data_ptr, 不需要自定义移动构造函数与移动赋值函数,可能是因为数据成员data已经定义了。

#include <iostream>
#include <vector>
#include <utility>                   
#include <algorithm>  
#include <memory>


class Listi {
public:
	typedef std::shared_ptr<std::vector<int>> d_sptr;
	std::vector<int> data;
	d_sptr data_ptr;

	Listi(){}
	
	Listi(const Listi& rhs ){
		data = rhs.data;
		this->data_ptr = d_sptr(new std::vector<int>);
		*(this->data_ptr) = *(rhs.data_ptr);
		printf("copy constructor\n");
	}

	Listi(Listi&& rhs ){
		if( this != &rhs ){
			data = std::move(rhs.data);
			this->data_ptr = rhs.data_ptr;
			rhs.data_ptr= d_sptr(new std::vector<int>); //= nullptr; reset()都可以,但是为了测试方便使用d_sptr(new std::vector<int>)
		}
		printf("move constructor\n");
	}
	
	
	Listi& operator=(Listi&& rhs){
		if( this != &rhs ){
			data = std::move(rhs.data);
			this->data_ptr = rhs.data_ptr;
			rhs.data_ptr = d_sptr(new std::vector<int>); 
		}
		printf("operator=(Listi&& rhs)\n");
		return *this;
	}
	

};

 
int main()
{
	printf("Test std::move !!! \n");
	Listi list1;
	list1.data = std::vector<int>(10,1);
	list1.data_ptr = Listi::d_sptr(new std::vector<int>(20,2));
	Listi list2 = std::move(list1);
	printf("list2 size_d =%d size_ptr=%d, list1 size_d=%d size_ptr=%d \n", list2.data.size(),list2.data_ptr->size(),list1.data.size(), list1.data_ptr->size());


	return 0;
}

cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
project(main)
add_compile_options(-std=c++11)
add_executable(main main.cpp)

运行结果

Test std::move !!! 
move constructor
list2 size_d =10 size_ptr=20, list1 size_d=0 size_ptr=0 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值