引用是一个已经存在的对象的别名。一旦一个对象初始化了这个引用,那么名称和引用都能够用来引用该对象。
int a =12;
int &ra=a; //ra是对象a的一个别名
--ra; //此时对象a=11
a=10; //ra=10
int* pa=&ra; //pa指针指向对象a
引用经常与指针混淆,可能是C++编译器常常将引用像指针一样使用,但是两者的机制是不同的。
引用和指针有3大区别:没有null的引用、所有的引用都必须初始化、引用总是引用已经被初始化的对象。由上面的例子我们能够看出,ra在它的生命周期中始终引用的对象a。对引用的错误用法经常是因为没有理解这3个区别。
一些编译器可能会捕捉到明显地创建一个null的引用的用法。
Employee &anemployee = *static_cast< Employee *>(0);//错误
然而,编译器也可能无法检测一些不明显的创建null引用的用法,在运行时这会导致“未定义的行为”的错误发生。
Employee *employee = getAnEmployee();//有可能返回null指针
Employee &anemployee = *employee;//这样的引用初始化为了null
if(&anemployee == 0)//出现错误:未定义的行为
一个引用变量必须初始化的要求暗示了引用初始化时它所引用的对象必须是存在的。这一点是非常重要的:一个引用是一个对象的别名,这个对象必须存在于引用初始化之前。一旦这个引用初始化引用一个对象,那么它就不能在引用别的对象了,在它的生命周期内它是与初始化的对象绑定的。别名的特性也正是引用经常作为形参的原因。如下:
template<typename T>
swap(T &a, T &b)
{ T temp(a);
a=b;
b=temp;
}
int a=1,b=2;
swap(a,b);//a=2,b=1
非常量的引用不能由常量来初始化,否则会提示类型转换错误。当然如果这个引用是const类型,那么同样能够有常量来初始化。
const int &a = 12;
const string &greeting = std::string(“Greeting!”);
当一个引用由常值初始化是,这个引用指向了一个临时的位置,这个位置有常值初始化。因此a并非是引用12,而是一个临时的int类型,该int类型初始化为了12。一般来说,这样的临时变量在构造它的表达式的结尾是要被销毁的,但是当这样的一个临时值初始化了一个const型的引用时,它的生命周期与引用变量相同。