1、 首先要认识到不存在类似于空引用(null reference)这样的东西。引用必须要指代某个对象,因此,如果有一个用于指代另外一个对象的变量,但是有可能没有这么一个可供指代的对象,这时就应该把这个变量声明成指针,因为这样就可以将它设为空值(null)了。反之,如果这个变量必须指代一个对象,即我们将这个变量设计成不允许为空值,那么这时基本上应该将这个变量声明成引用。
2、 因为引用总是要指代一个对象,C++要求引用必须初始化。
std::string& ace; //Err! 引用必须初始化
std::string sace(“rid”);
std::string& ace= sace; //Ok! 引用ace指向sace
指针并没有类似的限制:
std::string *ps; //未初始化的指针,合法但具有风险!
3、 不存在空引用这个事实说明,使用引用比使用指针更高效,因为在使用引用前不需要测试它是否有效:
void ACEPrint(const double& db)
{
std::cout<<db; //没有必要测试db,因为引用必然指向某个对象
}
而与之相反,指针通常需要检查其是否为空:
void ACEPrint(const double *db)
{
if(db) //检查是否是空指针
{
std::cout<<db;
}
}
4、 指针和引用另一个重要不同点在于:指针可以被重新赋值用以指向另外一个不同的对象;而引用则始终指向初始化时它所指代的对象:
std::string ace1(“hw”);
std::string ace2(“rid”);
std::string& race = ace1; //race指代ace1
std::string *pace = ace1; //pace指向ace1
race = ace2; //race依然指代ace1,但ace1的值现在是“rid”
pace = &ace2; //pace现在指向ace2,ace1的值不变
5、 总之,当需要考虑以下两种情况时,应该使用指针:有可能什么也不指向(这种情况下,我们可以把指针设为空值);需要能够在不同时候指向不同的对象(这种情况下可以更改指针的指向)。
当我们知道总是会指向某一个对象并且一旦指定那个对象,就不会再指向其他对象时,应该使用引用。
6、 还有一种情况应该使用引用,即当实现某些操作符的时候。最常见的例子是[]操作符。这个操作符通常需要返回一个值作为(下一个)赋值语句的赋值目标:
vector<int> v(10);
v[5] = 10;
我们可以看到,如果[]操作符返回一个指针,那么上面最后一条语句就必须写成:
*v[5] = 10;
这让v看起来像是一个关于指针的向量,而实际并非如此。因此,绝大多数情况下[]操作符都应该返回一个引用。
(P.S引用是一个现有对象的别名,用对象来初始化引用后,那么对象的名字或引用的名字都可以用于指向该对象。人们常常将引用和指针混淆,原因大概在于C++编译器通常采用指针的方式实现引用,但引用实际不是指针,其行为和指针并不相同。如前面所说,引用和指针存在三大区别:一是不存在空引用;二是所有引用都要初始化;三是一个引用永远指向用来对它初始化的那个对象。
一个指向非常量的引用是不可以用字面值或临时值进行初始化的:
double &aced = 13.4; //Err!
template<typename T>
void swap(T &a, T&b)
{
T temp(a);
a = b;
b = temp;
}
swap(std::string(“Hello”), std::string(“, World”)); //Err!
然而,一个指向常量的引用就可以:
const double &aced = 13.4; //OK!
template<typename T>
T add(const T &a, const T &b)
{
return a+b;
}
...
const std::string &greeting = add(std:string(“Hello”), std::string(“, World”)); //OK!
当一个指向常量的引用采用一个字面值来初始化时,该引用实际上被设置成指向“采用该字面值初始化”的一个临时位置。)