C++ 对值语义的支持, 导致程序员一不留神就会多出一次拷贝的过程既然选择了 C++ 这种坑多开发效率又低的语言, 一定是因为对于性能有比较高的需求(不追求性能, PythonJSJava 选哪个不好?), 所以对于无处不在的拷贝, 还是尽量避免为好
对于避免拷贝, 一般有以下两种做法:
引用最初在 C++ 中提出是为了能够更原生地处理运算符重载的结果, 比如
vec[1] = 10;
这里重载的运算符 [] 就会返回一个引用, 所以我们可以像一般变量一样对其赋值
说回利用引用避免拷贝, 一般可以检查如下三种情况:
a. 临时变量
对于嵌套很深的变量, 比如
object_a.GetAttrB()->GetAttrC()->GetAttrD()
如果这个变量在之后多次被用到, 一般地, 我们会选择用一个常量引用作为其别名, 避免一次拷贝
const TypeD& d = object_a.GetAttrB()->GetAttrC()->GetAttrD();
b. 函数参数
函数的形参绑定到实参时, 一般会将实参做一次拷贝这时, 对于那些在函数内不会被修改的参数, 可以使用常量引用, 避免这一次拷贝
c.map 插入
map 的插入经常会被写成这样
- A a;
- a.init();
- m[akey] = a;
这样的话就引入了一次拷贝
但是实际上, map 对于 [] 的重载保证了如果 key 不存在时, 会自动构建一个对象, 并返回其引用, 所以可以这样避免拷贝:
- A& a = m[akey];
- a.init();
移动, 而不是拷贝
移动语义是 C++11 中引入的, 表示资源的窃取转移, 而不是拷贝它和右值引用, std::move 结合, 可以减少拷贝的发生
例如:
vec[1] = std::move(objectA);
这里依赖如下几个概念, 得以实现移动语义, 而非拷贝 / 赋值语义
a.std::move 将左值转换为右值引用类型
b. 右值引用类型在进行重载函数匹配时, 将优先匹配参数为右值引用类型的移动构造函数或移动赋值函数
c. 移动构造 / 赋值函数用于移动资源(比如对象内部的指针), 并保证移动后的源对象可析构, 并且不再使用这些资源
d. 对于自定义类型, 移动构造 / 赋值函数一般需要自己实现; 对于标准库类型(如 string), 则已由标准库实现
因此, 对于一个后续不再使用的对象 objectA, 当我们想要窃取其资源 (如将其放入容器) 时, 可以通过 std::move + 右值引用 + 移动构造 / 赋值函数这一套组合拳, 避免资源的无谓拷贝
来源: https://juejin.im/entry/5abef75b5188251fc3296507