有没有想过一个问题,为什么C++ 有了指针( Pointer),仍然引进 “引用(Reference)” 这个概念?C++之父的解释:
C++ inherited pointers from C, so I couldn’t remove them without causing serious compatibility problems. References are useful for several things, but the direct reason I introduced them in C++ was to support operator overloading (简译: C++ 为了兼容 C,所以不可能会移除指针;引用在很多地方有用处,但主要是为了解决运算符重载 )
operator overloading (运算符重载)
运算符重载为什么必须要用到引用呢?我们看看下面一个实例,你就能理解为什么需要引用
#include <iostream> using namespace std; class Box { public: int length; Box(int len){ length = len; } //值方式重载 Box operator+=(const Box b) { Box box(0); box.length = this->length + b.length; return box; } //引用方式重载 Box& operator+=(const Box& b) { this->length = this->length + b.length; return *this; } //指针方式重载 Box* operator+=(const Box* b) { this->length = this->length + b->length; return this; } }; Box GetBox() { return Box(1); } int main() { Box box(1); //值方式重载示例 (box += box) += box; //result:1 //引用方式重载示例 (box += box) += box;//result:4 //指针方式重载示例 *(box += &box) += &box;//result:4 //只有引用方式的重载才能正确编译 box += GetBox(); cout << "box.Length = " << box.length << endl; return 0; }
值方式重载有两个问题:
- 构造函数被调用多次;浪费性能
- 功能未实现;(box += box) += box 结果不符合预期
指针方式重载也有以下两个问题:
- 写法不够简洁,晦涩难懂
- box += GetBox() 编译不过(不能用 “右值” 当实参)
函数参数
C++ 之父说还说,引用在有些地方也很有用。除了重载运算符,作为函数参数也很有用,示例如下:
void Func(Foo foo);//有拷贝构造,不推荐 void FuncPtr(Foo* foo);//无拷贝构造,但是foo使用之前需判空 void FuncRef(const Foo& foo);//无拷贝构造,官方推荐(可传入右值实参)
你是不是以为就这了?其实它还有个隐藏的用处,请看以下代码:
void foo(const int& value) { int temp = value + 1; } void bar(const int* value) { int temp = *value + 1; } int main() { int value = 1; int& ref = value; int* ptr = &value; foo(2); foo(value); foo(ref); bar(2); //error bar(value); //error bar(ptr); //error return 0; }
函数 foo(const int& value) 可以接受三种实参, 这一点在模板里面很有用 ;而 bar(const int* value) 只能接受指针类型实参
函数返回值
还有没有其它用处呢?我觉得写单例的时候会用到。以下是 C++ 单例的 “标准” 写法:
class Singleton { private: Singleton (void) public: static Singleton& getInstance() { static CSingleton m_pInstance; return m_pInstance; } };
为什么引用的单例比指针的更好?因为安全,返回的是引用不可被更改,而且不会为空
总结
综上,引用的作用有以下地方:
- operator overloading;不仅是为了简化语法,还是为了实现功能
- 作为函数形参
- 作为函数返回值
当然,我们这里讨论的都是普通引用,也就是左值引用;C++ 11以后引入了右值引用,初看很迷惑,实际也是为了解决问题,下一篇我们再来谈谈