右值引用允许编程人员去避免不必要的内存拷贝,从而提高性能。
我们知道如果一个类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只是返回一个右值类型,从而调用类中实现的对右值的操作。所以关键还是类的实现,对写库的人而言增加了工作量, 但是对用户而言还是很容易使用的,而且极大的提高了程序性能。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

鄂ICP备17003086号. Copyright © 2016. All Rights Reserved.