C++自古以来, 针对函数参数类型的传递一直都有一些约定俗成的规定, 比如:
针对原始数据类型,直接使用传值形式传参
void test(int number);void test(bool enable);void test(double percent);
而针对结构体以及类, 通常都是:
如果无需改变参数的值, 使用常量引用或者常量指针
如果需要改变参数的值, 使用引用或指针
如果需要拷贝参数的值,使用常量引用
示例代码
struct A {/// ...};class B {public: /// ...private: /// ...};void test_without_changed(const A& a);void test_without_changed(const B* b);void make_changed(A* a);void make_changed(B& b);A copy(const A& a);B copy(const B& b);
但是, 自动C++11之后这些或许就有一些变化了
在C++11开始, 引入了一个新的类型: 右值引用
何为右值引用? 说通俗点就是出现在等号右边的值的一个引用
与右值引用相对的肯定会有左值引用,即 可以出现在等号左边的值的引用
这俩家伙具体有啥区别呢? 这里我就不多讲了, 自个儿查取咯
有了这个右值引用啊, C++11开始引入了一种新的拷贝: 移动拷贝
什么是 移动拷贝呢?
移动拷贝就是在拷贝出一个新的对象时, 将旧对象的值(内部资源)直接移动到新对象,旧对象直接变为一个(资源全部转移走了的)空对象
这又什么好处呢?
你看啊, 假设我有一个对象, 管理着一块相当大的内存,比如1G, 由于某种原因, 这个对象生命周期即将结束了, 但是该对象所管理的内存我还不想那么早释放掉, 我可以先使用移动构造将管理的内存块转移出去, 然后在其他地方继续使用新的对象来管理这块内存, 多好啊, 避免了资源释放再重新申请的开销
那再有了移动构造以后, 我们应该使用什么形式的传参更好呢?
不需要拷贝
不需要修改参数的值 —— 使用常量引用或者常量指针
需要修改参数的值 —— 使用非常量引用或者指针
需要拷贝 —— 使用传值
示例代码
struct Some { /// ...};/// 不拷贝,也不需要修改参数的值void test_without_copy_and_change(const Some& some);void test_without_copy_and_change(const Some* some);/// 不拷贝, 但是需要修改参数的值void change(Some& some);void change(Some* some);/// 拷贝Some copy_and_change_some_options(Some some); /// why ?
为什么需要拷贝的时候 只需要用一中形式的传参呢?
仔细想一想, 我们在参数传值的时候会发生什么 ? 当然会自动拷贝啊
对呀, 参数传值, 自动拷贝呀, 多简单
那为啥不写成下面这种呢?
Some copy_and_change_some_options(const Some& some);
在C++11之前,确实这样就可以了, 但是到了C++11之后, 上面的拷贝是不是还遗漏了什么呢?
除了 复制拷贝, 当然还得有个移动拷贝啊
那就是下面这样咯:
Some copy_and_change_some_options(const Some& some);Some copy_and_change_some_options(Some&& some);
这多麻烦啊, 每多一种, 旧都要写两种, 像我这么懒的人, 怎么可能会每次都写这么多???
当然是直接写成一种啊
Some copy_and_change_some_options(Some some);
为啥这一种就能处理上面两种呢?
我们首先看复制构造
但我们直接传递一个对象做参数的时候, 是不是结果就跟复制拷贝一样了, 同样都是一次复制构造的开销
Some copy_and_change_some_options(const Some& some){ /// 一次copy /// ... }Some copy_and_change_some_options(Some some) /// 直接在传值的时候一次copy{ /// ...}
然后再看移动构造
传值的时候, 的确都会调用一次构造函数, 但是并不是说, 传值只会调用复制构造呀
我们传值的时候, 把参数改成右值引用形式看看
Some copy_and_change_some_options(Some&& some){ /// 一次move /// ...}Some copy_and_change_some_options(Some some) /// 传参时使用std::move(...) 一次move{ /// ...}
果然开销一样
当不需要拷贝对象的时候, 我们使用引用或指针形式的参数, 如果不需要修改参数对象的值, 我们再加上const 修饰, 防止意外修改当需要拷贝参数对象的时候, 我们直接使用传值形式的参数, 这样既可以使用复制拷贝, 又可以使用移动拷贝, 很方便的合并了常量引用参数和右值引用参数
联系客服