左值引用
左值引用是指针的语法糖,和指针作用相同,两者的区别主要是:
引用必须立即初始化,但是指针可以先初始化为空指针
对引用取地址得到的是原变量的地址,对指针取地址得到的是指针变量的地址
对引用 sizeof 得到的是对象的大小。对指针 sizeof 得到的是指针的宽度
引用无法更换指向目标,但是指针可以
如果一个引用指向的对象已经销毁,那么这个引用就变成了一个悬空引用。访问它是未定义行为。 |
移动语义
移动语义可以用来转移对象的所有权,这一操作是通过移动构造函数实现的。std::move
通过将对象强转转为右值引用的形式请求调用移动构造函数,从而触发移动语义。
移动构造函数的目的是转移对象所有权。所有权转移的实现本质上是通过 memmove 和 memset 实现的。
如果是使用右值赋值操作,则还需要释放自己持有的资源。
移动语义对于 POD 对象没有意义。
- 移动语义是为了管理所有权而不是提高性能。
完美转发
现在假设有这样一个工厂函数:它构造指定类型,并返回其智能指针。现在假设此对象只有一个参数,那么代码假设是这样:
template<class T, class A1>
std::shared_ptr<T> factory(const A1& a1) {
return std::shared_ptr<T>(new T(a1));
}
调用方式为:
std::shared_ptr<A> p = factory<A>(5);
但是如果 A 的构造函数需要的参数是非 const 引用呢?这种情况下就会导致编译错误。
因此修改后的代码为:
template<class T, class A1>
std::shared_ptr<T> factory(A1& a1) {
return std::shared_ptr<T>(new T(a1));
}
现在 const 版本和非 const 版本都能用了。
请注意:使用这种方式可以解决参数转发时的 cv 限定类型。事实上,这正是当今转发应用程序的编码方式(例如 std::bind ) |
但是又带来一个新问题:无法传递右值,下述这样的代码无法编译:
std::shared_ptr<A> p = factory<A>(5); // 错误
A* q = new A(5); // 允许
因此最佳的方式是使用右值引用作为参数:
template<class T, class A1>
std::shared_ptr<T> factory(A1&& a1) {
return std::shared_ptr<T>(new T(a1));
}