当前位置:   article > 正文

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

std list 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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
project(main)
add_compile_options(-std=c++11)
add_executable(main main.cpp)
  • 1
  • 2
  • 3
  • 4

运行结果

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

  • 1
  • 2
  • 3
  • 4
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/640105
推荐阅读
相关标签
  

闽ICP备14008679号