您的位置:首页 > 编程语言 > C语言/C++

改善C++ 程序的150个建议学习之建议26:用引用代替指针

2013-09-13 09:09 483 查看
建议26:用引用代替指针

指针,可以通向内存世界,让我们具备了对硬件直接操作的超级能力。C++意识到了强大指针所带来的安全隐患,所以它适时地引入了一个新概念:引用。引用,从逻辑上理解就是“别名”,通俗地讲就是“外号”。在建立引用时,要用一个具有类型的实体去初始化这个引用,建立这个“外号”与实体之间的对应关系。

对于引用的理解与使用,主要存在两个的问题:它与指针之间的区别。未被充分利用。引用并非指针。引用只是其对应实体的别名,能对引用做的唯一操作就是将其初始化,而且必须是在定义时就初始化。对引用初始化的必须是一个内存实体,否则,引用便成为了无根之草。一旦初始化结束,引用就是其对应实体的另一种叫法了。与指针不同,引用与地址没有关联,甚至不占任何存储空间。代码如下所示:

int iNum = 12;

int &rNum = iNum;

int *pNum = &rNum; // 等同于int *pNum = & iNum;

iNum = 2011; // rNum的值也为2011

由于引用没有地址,因此就不存在引用的引用、指向引用的指针或引用的数组这样的定义。据说尽管C++标准委员会已经在讨论,认为应在某些上下文环境里允许引用的引用。但那都是将来的事,至少现在不可以,将来的事谁又说得准呢?因为是别名,与实体所对应,所以引用不可能带有常量性和可挥发性。所以,下面的代码在编译时会出现问题:

int r = 10;

int & volatile s = r;

int & const m = r;

volatile int& t = r;

const int& n = r;

之所以说是出现问题,而不是错误,最主要的原因是各厂商编译器对于上述语法的容忍程度不同,有的会直接抛出错误并且编译失败,有的却只给出一个警告,比如:gcc 4.3给出错误

error: volatile/const限定符不能应用到'int&'上

VC++ 2010给出警告

warning C4227: 使用了记时错误: 忽略引用上的限定符

对于加在引用类型前面的const或volatile修饰词,它们是符合编译器规则的,或者说被编译器选择性忽略了,没有什么问题(无error或warning)。而对于指针上述使用绝对不存在任何的问题。C阵营中那帮“顽固派”习惯在C++工程里使用指针,并且以此为傲,现在该是为引用翻身的时候了。先看一个简单的示例:

void SwapData1(int a, int b)

{

int temp = a;

a = b;

b = temp;

}

void SwapData2(int* a, int* b)

{

int temp = *a;

*a = *b;

*b = temp;

}

void SwapData3(int& a, int& b)

{

int temp = a;

a = b;

b = temp;

}

上述代码要实现的功能极为简单,就是交换两个数据的值。SwapData1()是不能实现所设定的功能的,主要原因是函数内交换的只是实参的副本;而SwapData2()和SwapData3()则正确地实现了作者意图,其汇编代码如下:

SwapData1(a,b);

00F431EC mov eax,dword ptr [b]

00F431EF push eax

00F431F0 mov ecx,dword ptr [a]

00F431F3 push ecx

00F431F4 call SwapData1 (0F4114Fh)

00F431F9 add esp,8

SwapData2(&a,&b);

00F431FC lea eax,[b]

00F431FF push eax

00F43200 lea ecx,[a]

00F43203 push ecx

00F43204 call SwapData2 (0F411EFh)

00F43209 add esp,8

SwapData3(a,b);

00F4320C lea eax,[b]

00F4320F push eax

00F43210 lea ecx,[a]

00F43213 push ecx

00F43214 call SwapData3 (0F4101Eh)

00F43219 add esp,8

正如汇编代码中所示的那样,SwapData2()和SwapData3()其实是一样的,都是对源数据进行操作。但是相较于SwapData2()而言,SwapData3()的实现代码更加简洁清晰。这就是引用在传递函数参数时所具有的巨大优势。再来看函数返回值方面,如果其返回值是引用类型,那么就意味着可以对该函数的返回值重新赋值。就像下面代码所示的数组索引函数一样:

template <typename T, int n>

class Array

{

public:

T &operator [](int i)


{

return a_[i];

}

// ...

private:

T a_
;

};

Array<int, 10> iArray;

for(int i=0; i<10; i++)

iArray[i] = i*2;

当然,上述代码可以使用指针重新实现,并且可以保证实现相同的功能,但是相比指针实现版,引用返回值使对数组索引函数的操作在语法上颇为自然,更容易让人接受。也许有人认为,指针功能更强大,因为还有指向数组的指针、指向函数的指针,这里我要说的是,引用同样可以。引用在指向数组时还能够保留数组的大小信息,关于这方面的内容,在此就不多讲了。

请记住:从编码实践角度来看,指针和引用并无太多不同。在大多情况下,指针可由索引类型完美代替,并且其实现代码更简洁清晰,更加易于理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐