文章标题 原创 翻译 转载 文章内容 右值引用允许编程人员去避免不必要的内存拷贝,从而提高性能。 我们知道如果一个类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只是返回一个右值类型,从而调用类中实现的对右值的操作。所以关键还是类的实现,对写库的人而言增加了工作量, 但是对用户而言还是很容易使用的,而且极大的提高了程序性能。 文章类别 Python Mobile Android Java Shell Life Database Bug Windows IOS Tools Boost Node.js Mac Product Tips C/C++ Golang Javascript React Qt MQ MongoDB Design Web Linux LLM ChatGPT RAG AI 提交