- 在任何情况下都不能用指向空值的引用。
- 一个引用必须总是指向某些对象。
- 因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。
- 相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。
“但是,请等一下”,你怀疑地问,“这样的代码会产生什么样的后果?”
char *pc = 0;// 设置指针为空值
char& rc = *pc;// 让引用指向空值
这是非常有害的,毫无疑问。结果将是不确定的(编译器能产生一些输出,导致任何事情都有可能发生)。
因为引用肯定会指向一个对象,引用应被初始化。
string& rs;// 错误,引用必须被初始化
string *ps;// 指针没有这样的限制,未初始化的指针,合法但危险
string s("xyzzy");
string& rs = s;// 正确,rs是s的引用
不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。
void printDouble(const double& rd)
{
cout << rd; // 不需要测试rd,它肯定指向一个double值
}
void printDouble(const double *pd)
{
if (pd) //相反,指针则应该总是被测试,防止其为空:
{
cout << *pd;
}
}
指针与引用的另一个重要的不同:是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。
string s1("Nancy");
string s2("Clancy");
string& rs = s1; // rs 引用 s1
string *ps = &s1; // ps 指向 s1
rs = s2; // rs 仍旧引用s1,但是 s1的值现在是 "Clancy"
ps = &s2; // ps 现在指向 s2; s1 没有改变
在以下情况下你应该使用指针:
- 一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),
- 二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。
还有一种情况,就是当你重载某个操作符时,你应该使用引用。最普通的例子是操作符[].这个操作符典型的用法是返回一个目标对象,其能被赋值。
vector<int> v(10); // 建立整形向量(vector),大小为10;
v[5] = 10; // 这个被赋值的目标对象就是操作符[]返回的值
//如果操作符[]返回一个指针,那么后一个语句就得这样写:
*v[5] = 10;
但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用。
当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针
假设你有
void func(int* p, int& r);
int a = 1;
int b = 1;
func(&a,b);
指针本身的值(地址值)是以pass by value进行的,你能改变地址值,但这并不会改变指针所指向的变量的值
p = someotherpointer;//a is still 1
但能用指针来改变指针所指向的变量的值
*p = 123131; // a now is 123131
引用本身是以pass by reference进行的,改变其值即改变引用所对应的变量的值
r = 1231;// b now is 1231
尽可能使用引用,不得已时使用指针。
当你不需要“重新指向”时,引用一般优先于指针被选用。这通常意味着引用用于类的公有接口时更有用。引用出现的典型场合是对象的表面,而指针用于对象内部。
什么时候需要使用引用?
- 程序员能够修改调用函数中的数据对象
- 通过传递引用而不是整个数据对象,可以提高程序的运行速度。
当数据对象较大时(如结构和类对象),第二个原因最重要,这些也是使用指针参数的原因。这是有道理的,因为引用参数实际上是基于指针的代码的另一个接口。