右值引用与move浅拷贝的类实现
Right Value Reference && Move-aware Class
一、背景
一直以来对右值引用了解不太深入,看了侯捷大神的c++11视频茅塞顿开,喜形于色。把心得体会和自己的理解做一下记录和梳理,以便于以后查阅。
二、右值引用的目的
减少不必要的内存拷贝。我们在写代码的过程中经常会发生无意识的拷贝,也就是说在你不经意的情况下产生了拷贝动作。
举例
std::vector<string> v;
string s("hello world!");
v.push_back(s);
上述代码时往一个string类型的vector容器中放入一个string对象,在push的时候发生了一次拷贝。
另外,vector在不断的push过程中,其容量会动态成长,成长过程也会发生大量隐形的拷贝。
三、右值引用的表现形式
1. 临时对象
2. 用move关键字告诉编译器把左值引用转成右值引用
注意: 使用move关键后,要保证对象不会在其他的地方被使用,因为内容已经被steal(形象的描述为偷走)了。
四、通过MyString对move浅拷贝加深理解
class MyString
{
public:
MyString(const MyString& str) { // copy 构造函数
...
}
MyString(MyString&& str) noexcept { // move 构造函数
...
}
private:
char* data_;
}
在生成对象时,当传入左值引用,调用的时copy构造函数;当传入右值引用时,则调用move构造函数。
注:对于成长型容器vector,需要对move构造函数加上noexcept关键字。
五、 完美的传递
void process(int& i) {
cout << "process(int&): " << i << endl;
}
void process(int&& i) {
cout << "process(int&&): " << i << endl;
}
void forward(int&& i) {
cout << "forward(int&&): " << i << endl;
process(i);
}
// 测试
int a = 0;
process(a); // process(int&): 0
process(1); // process(int&&): 1 --temp对象被当作右值引用
process(move(a)); // process(int&&): 0 --强制把左值引用改为右值引用
forward(2); // forward(int&&): 2, process(int&): 2 --不完美的传递,右值变成了左值
forward(move(a)); // forward(int&&): 0, process(int&): 0 --同上
// 实现完美传递
void forward(int&& i)
{
cout << "forward(int&&): " << i << endl;
proccess(std::forward<int>(i));
}
// 下图为标准代码库中的实现:
六、Move-aware class的实现
模拟string写一个简单的MyString,实现move-aware的功能
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class MyString
{
public:
MyString()
{
data_ = new char[100];
len_ = 99;
cout << "default ctor" << endl;
}
MyString(const MyString& str)
:len_(str.len_)
{
data_ = new char[100];
memcpy(data_, str.data_, str.len_);
cout << "Copy ctor" << endl;
}
MyString(MyString&& str) noexcept
:data_(str.data_), len_(str.len_)
{
str.data_ = nullptr;
cout << "Move ctor" << endl;
}
MyString& operator=(MyString& str)
{
cout << "Copy asign" << endl;
if (data_)
{
cout << "Copy asign delete" << endl;
delete data_;
}
data_ = new char[100];
len_ = str.len_;
memcpy(data_, str.data_, str.len_);
return *this;
}
MyString& operator=(MyString&& str) noexcept
{
cout << "Move asign" << endl;
if (data_)
{
cout << "Move asign delete" << endl;
delete data_;
}
data_ = str.data_;
len_ = str.len_;
str.data_ = nullptr;
str.len_ = 0;
return *this;
}
~MyString()
{
cout << "dtor" << endl;
if (data_)
{
cout << "delete" << endl;
delete data_;
}
}
private:
char* data_;
size_t len_;
};
int main()
{
vector<MyString> v;
MyString dd;
v.push_back(move(dd));
return 0;
}