右值引用(Rvalue)

右值引用(Rvalue)

可以大幅改善容器的性能
当对象内部含有指针时,可以使用右值来窃取对象

原理

当右边的对象是一个右值时,左侧的对象可以偷取右侧对象的资源

1
2
3
4
//c11之前不可以对右值取地址
int x=foo()
int* p=&foo()//Error :对函数的返回值取地址
foo()=7;//Error:对函数的返回值复制

右值引用(Rvalue)

可以大幅改善容器的性能

原理

当右边的对象是一个右值时,左侧的对象可以偷取右侧对象的资源

1
2
3
4
//c11之前不可以对右值取地址
int x=foo()
int* p=&foo()//Error :对函数的返回值取地址
foo()=7;//Error:对函数的返回值复制

cpp 11后我们认为右值的对象时可以被窃取资源的
为此我们需要:

  1. 必须有语法让我们在调用端诉编译器,这是个Rvalue
  2. 必须有语法放我们在被调用段写出一个专门处理Rvalue的所谓move assignment函数

code

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyString{
private:
char* _data;
public:
//copy actor(构造)
MyString(const Mystring& str):initialization list{

}
//move actor(构造)
Mystring(MyString&& str)noexcept:initialization list{
...
}
}

G2.9

1
2
3
4
5
6
7
8
9
10
11
iterator insert(iterator posistion,const T&x){
size_type n=posistion-begin();
if(finish !=end_of_storge && position ==end()){
construct(finish,x);
++finish;
}else{
insert_aux(position,x);
}
return begin()+n;
}

G4.9

1
2
3
4
5
6
7
iterator
insert(const_iterator __position,const value_type& __x);

iterator
instert(const_iterator __position,value_type&& __x){
return emplace(__position,std::move(__x))
}

旧版问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void process(int& i){
cout<<"process(int &i):"<<i<<endl;
}

void process(int&& i){
cout<<"process(int&&):"<<i<<endl;
}

void forward(int&& i){
out<<"forward(int&&):"<<i<<endl;
process(i);
}
int a=0;
forward(2);//foward(int&&):2 -> process(int&):2 因为在传导过程中变为了左值
forward(move(a));

改进版本

标准库提供的完美的传递过去

1
2
3
4
5
6
//bits/move.h 下
template <typename T1,typename T2>
void functionA(T1&& t1,T2&& t2){
funtionB(std::forward<T1>(t1),
std::forward<T2>(t2))
}

使用时要实现一下功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private:
char* _data;
public:
//copy actor(构造)
MyString(const Mystring& str):initialization list{

}
//move actor(构造)
Mystring(MyString&& str)noexcept:initialization list{
...
}
// copy assignment(赋值)
MyString& operator=(const MyString& str){

return *this;
}

//move assignment(赋值)
Mystring& operator=(MyString&& str){
noexcpt{
return *this;
}
}

例子

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class MyString{
public:
//用于测试的静态变量,实际类中不需要
static size_t DCtor;
static size_t Ctor;
static size_t CCtor;
static size_t CAsgn;
static size_t MCtor;
static size_t MAsgn;
static size_t Dtor;
private:
char* _data;
size_t _len;
void _init_data(const char*s){
_data = new char[_len+1];
memcpy(_data,s,_len);
_data[_len]='\0';
}
public:
//default constructor
MyString():_data(NULL),_len(0){++DCtor;}
//constructor
MyString(const char* p):_len(strlen(p)){
++Ctor;
_init_data(p);
}

//copy constructor
MyString(const Mystring& str):_len(str._len){
++CCtor;
_init_data(str._data);
}
//move construct,with noexcept
MyString(Mystring& str)noexcept:_data(str._data),_len(str._len){
++MCtor;
str._len=0;
str._data=NULL;//解引用
}

//copy assignment
MyString& operator=(const MyString& str){
++CAsgn;
if(this != &str){
if(_data) delete _data; //源代码使用的是delete _data,我认为错误,后发现。所引用的数组已被转移,只delete 即可
_len=str._len;
_init_data(str._data);//COPY!
}else{

}
return *this;
}

//move assignment
MyString& operator=(MyString&& str) noexcept{
if(this !=& str){
if(_data) delete _data;
_len = str._len;
_data=str._data;//MOVE
str._len=0;
str._data=NULL//重要
}
}

//析构函数,默认就是noexcept 的
virtual ~MyString(){
++Dtor;
if(_data){
delete _data;
}
}

//为了set 可以使用
bool
operator<(const MyString& rhs)const {
return string(this._data)<string(rhs._data);
//借用现成实现,std::string 可以比较大小
}

bool
operator=(const MyString& rhs)const{
return string(this._data)=string(rhs._data);
//借用现成实现,std::string 可以判断是否相等
}

char* get()const{return _data;}
};

对于性能的影响

  • vector
    • 插入元素,影响巨大,是否实现移动构造
    • 容器拷贝,影响巨大,需要使用std::move()
    • 容器交换,影响巨大,需要使用std::move()
  • deque
    • 插入元素,影响不大 (不一定,和元素的位置相关)
    • 容器拷贝,影响巨大,需要使用std::move()
    • 容器交换,影响巨大,需要使用std::move()
  • list 链表
    • 插入元素,影响不大
    • 容器拷贝,影响巨大,需要使用std::move()
    • 容器交换,影响巨大,需要使用std::move()
  • multiset(红黑树)
    • 插入元素,影响不大
    • 容器拷贝,影响巨大,需要使用std::move()
    • 容器交换,影响巨大,需要使用std::move()
  • unordered_multiset(hash_table)
    • 插入元素, 差别不大
    • 容器拷贝,影响巨大,需要使用std::move()
    • 容器交换,影响巨大,需要使用std::move()

原理

  1. 容器拷贝
    1. 如果使用拷贝构造,会进行内存的分配,并且拷贝每一个内容
    2. 如果使用移动构造,只更改对应的容器的指针。//常量的时间