Effective C++ 炒冷饭 - Item 21 该返回值的时候别返回引用

Effective C++ 炒冷饭 - Item 21 该返回值的时候别返回引用

[原创文章欢迎转载,但请保留作者信息]
Justin 于 2009-12-19

在认真学习第20课后,大师有点担心学生马上变成传引用(pass-by-reference)的粉丝。为了辩证的看待问题,下一课现在开讲……
初看题目,第21条玉律说的也是一个很简单的情况:如果函数需要返回一个对象,就不要仅仅返回引用,因为返回的引用有可能已经指向一个不存在的对象了。
没错,不过细读下去发现还是有一些的细节自己原来并没有考虑到。

如果函数返回的是值(对象),那么就难免要进行创建对象操作(构造/析构)来存储返回的结果;如果是返回引用,就可以避免这额外的对象创建,节省了资源和时间。
看来,似乎返回引用要优于返回整个对象。

是这样么?大师继续揭开更本质的一些东西:
如果一个函数可能返回一个对原来不存在的对象的引用,那么函数就要自己去创建这个对象:要么在栈上(stack)要么在堆上(heap)。

  • 第一种情况中,函数中定义了局部对象,然后返回对该对象的引用。对象在函数结束后自动销毁,引用指向无效的地址。
    OK,很简单。
  • 第二种情况,函数使用new动态创建了一个对象,然后返回对该对象的引用。粗看没有问题,因为这个返回的引用还是有效的。
    但是细想就会发现:我们能确保这个对象被正确的收回(delete)吗?
    • 书中举了一个很好的例子:一个*运算符函数,接受两个乘法运算数,返回一个积。
      如果在函数中动态创建一个对象来存储乘积,并返回对这个新对象的引用,那么下面的计算就会带来内存泄漏:
         Y=A*B*C
      因为在这个“连续”乘法中,有两个“乘积对象”被创建,但是我们丢失了第一次乘法创建的对象的指针。
      所以这样的做法是不妥的。

也许大师有被问过:那么对于第一种情况我们可不可以返回一个静态(static)对象的引用?书中用了同样的例子来回答:NO。
   if (A*B == C*D) {//..}
如果返回静态对象的引用,上面的判断语句永远得到true值,因为“==”号两边的运算结果是指向同一块数据的引用。

不知道是不是后面又有刨根问题的学生追问:那我能不能用一个静态对象的数组来存放不同此运算的结果?大师懒得举例子了,我猜想也没必要:这样的方案带来的副作用及其开销本身就已经大于原来要解决的问题了吧?

最后总结本课中心思想:

  1. 不要尝试在函数中返回对局部对象(存储于栈)的引用,也不要对动态创建的对象(存储于堆)做同样的蠢事,而如果真有打算、非常渴望、十分想要返回对静态对象的引用,在这么做之前也要考虑清楚会不会有上面例子中的情况出现。
  2. 至少,返回对象不会有上面列出的种种错误危险,仅仅是有可能带来创建额外对象的开销而已,而这个开销的可能还有可能被排除,如果你用的编译器足够聪明的话。

下课!
谢~谢~老~师~

你可能感兴趣的:(Effective C++ 炒冷饭 - Item 21 该返回值的时候别返回引用)