std move和右值引用

Table of Contents

    右值引用允许编程人员去避免不必要的内存拷贝,从而提高性能。
    我们知道如果一个类A的成员变量中有指针,那么就要考虑深拷贝和浅拷贝了,深拷贝通常要实现下面几个函数:

    • 构造函数
    • 拷贝构造
    • 赋值操作符

    这样做是没问题的,但是会带来一个问题,会造成一些没必要的拷贝,如:

    std::string str("hello");
    str.resize(1024 * 1024 * 100);
    
    std::vector<std::string> v;
    v.push_back(str);
    

    str这里故意给它分配了100M,把它push到vector中的时候其实又做了一次额外的拷贝(也就是深拷贝), 我们通过任务管理器中的内存也可以看出来有200多兆了,拷贝是非常高昂的开销,使用下面方法来解决这个问题:

    std::string str("hello");
    str.resize(1024 * 1024 * 100);
    
    std::vector<std::string> v;
    v.push_back(std::move(str));
    

    我们再通过任务管理器看程序运行后的内存大小发现就100兆多一点,说明上面方法避免了深拷贝。
    其实它是用了类似浅拷贝的方式直接把str中的内存地址给拷贝过来了,同时把str指向nullptr,所以,此时 打印str的内容发现它是空的。这样做是非常有必要的,否则两个指针指向同一块内存会造成更严重的危害, 况且std::move通常针对的是右值(相当于临时值),所以move之后我们不太可能会再去使用它。

    上面我们就加了一个std::move就有这么大的优化,看起来很容易的。
    其实不然,因为我演示用的是STL中的string在c++11中它自身就支持右值引用
    截取一段源码,&&就是右值引用

    basic_string(_Myt&& _Right) _NOEXCEPT
    : _Mybase(_Right._Getal())
    {	// construct by moving _Right
    _Tidy();
    _Assign_rv(_STD forward<_Myt>(_Right));
    }
    

    所以,如果是我们自定义的类要想使用右值引用就要像实现拷贝构造,赋值操作符那样去实现右值引用了,举个例子:

    template <class T>
    class clone_ptr
    {
    private:
        T* ptr;
    public:
        // 构造函数
        explicit clone_ptr(T* p = 0) 
        : ptr(p) 
        {}
    
        // 析构函数
        ~clone_ptr() 
        {
          delete ptr;
        }
    
        // 拷贝构造
        clone_ptr(const clone_ptr& p)
            : ptr(p.ptr ? p.ptr->clone() : 0) 
        {}
    
        // 赋值操作符
        clone_ptr& operator=(const clone_ptr& p)
        {
            if (this != &p)
            {
                delete ptr;
                ptr = p.ptr ? p.ptr->clone() : 0;
            }
            return *this;
        }
    
        // 拷贝构造,move语法,右值引用
        clone_ptr(clone_ptr&& p)
            : ptr(p.ptr) 
        {
    	p.ptr = 0;
        }
    
        // 赋值操作符,move语法,右值引用
        clone_ptr& operator=(clone_ptr&& p)
        {
            std::swap(ptr, p.ptr);
            return *this;
        }
    
        // Other operations
        T& operator*() const {return *ptr;}
        // ...
    };
    
    

    std::move只是返回一个右值类型,从而调用类中实现的对右值的操作。所以关键还是类的实现,对写库的人而言增加了工作量, 但是对用户而言还是很容易使用的,而且极大的提高了程序性能。