右值引用与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;
}   
posted @ 2020-11-15 21:18  tommy-weng  阅读(101)  评论(0编辑  收藏  举报