为什么C++中千万不要返回局部对象或变量的引用和指针

大家都知道一个常识:“C++中千万不要返回局部对象或变量的引用和指针”。

既然所有C++权威的书上都要求“一定不要返回局部对象或变量的引用和指针”,那为什么C++编译器不从语法上直接禁掉这种用法。如果只是建议的话,那么“返回局部对象或变量的引用和指针”是否有用武之地呢?(从理论上来讲,我认为这种做法似乎总是错误的,原因大家都知道。)
例1:
class CComplex

{

public:

         CComplex():real(0),image(0){}

         CComplex(doublereal,double image):real(real),image(image){}

 

         CComplex& operator+(const CComplex& second)

         {

                   CComplextemp(real+second.real,image+second.image);

 

                   returntemp;

         }

 

         voidPrint()

         {

                   cout << "(" << real << "+" << image << "i)" << endl;

         }

 

private:

         doublereal;

         doubleimage;

};

 

int main()

{

         CComplex a(2,4);

         CComplex b(1.5,3.5);

 

         CComplex c=a+b;

         c.Print();

 

         return0;

}

 

  operator+返回的是临时对象的引用,为什么能正确地工作???

例2
string& f()

{

         string s("hello");

 

         returns;

}

 

int main()

{

         cout << f() << endl;

 

         return0;

}


  同样是对象,为什么string对象就不行,就因为string比较特殊???

例子3
double& f()

{

         doubled(5.55);

 

         returnd;

}

 

int main()

{

         cout << f() << endl;

 

         return0;

}

  为什么内置类型(int,float等均可)返回局部变量的引用总可以正确地工作???


  operator+返回的是临时对象的引用,为什么能正确地工作???
  答:main函数在执行之后,a,b入栈,接着a+b调用了operator+,temp也入栈,operator+执行完后,temp出栈并调用析构函数,由于出栈仅仅是移动了PC指针,而你又未写析构函数将CComplex清零,因此temp所占的那块栈空间的内存依然保持原样,只是PC指针已经不再指向它,而operator+返回的引用其实指向的是temp所占内存,然后在调用CComplex的默认拷贝构造的函数的时候,由于拷贝构造函数的输入参数也是引用,因此也指向temp那块内存,对此快内存也会按照CComplex类型来进行访问,最后c就得到了temp的内容。这里即使是写成CComplex& c=a+b;结果也是能输出temp的内容的。此时你若在此句话后面再加几个函数调用,这些函数必须要有参数或内部定义有变量,然后再c.Print(),你会发现结果完全变了。

  同样是对象,为什么string对象就不行,就因为string比较特殊???
  答:因为s在出栈的时候其析构函数会将内存都清掉,在外面还想访问自然访问不成功了。

  一、为什么不禁用的问题
  为什么不禁引用返回局部变量,技术上真的是不难吗?且,有足够的必要吗?请见以下例子:

int *f1(int &ri)

{

         return&ri;

}

 

int *f2()

{

         inti=4;

         int *j;

 

         j=f1(i);

 

         returnj;

}

 

int main()

{

         int*p=f2();

 

         *p=6;

 

         return0;

}


  p在初始化后,*p生命期是否已经结束了呢?我相信,如果这件事也得由编译器去判断,那么显然,程序员全部可以下岗了,编译器实在是太智能了,人还有必要存在吗?但现有技术真的能吗?如果能的话,要花多大开销,这个开销有必要吗?“千万不要返回局部对象或变量的引用和指针”应该是个原则性的东西,它是个典型代表,其实大原则是“不要在自动变量(不管是表达式中间结果的临时变量(如果它不能保证总优化到寄存器中)还是源程序中有明确名字的auto变量)生命期结束后还试图解引用它”。

  程序设计语言课一般会说语言的可写性与可读性是对矛盾,C语言的可写性特别强,既会给比较强的人非常灵活的选择,又会让入门者走不少弯路或者半途而废。利器不是谁都能用得好,这与水平不水平没什么关系,说人的水平不足够使用C++,当然也可以站在没有学会用C++的人的立场,说C++太过于复杂,以至大多数人是学不会用不好的,但它的每个设计的确都有它的现实考虑,编程语言是很实在的东西,往往外貌冷冰冰但其为什么是这样有充足原因。

  二、你的好运气

  你要是明白函数调用时局部变量是如何入栈出栈的,看看反汇编的代码,并跟踪一下堆栈的变化情况,你会设计出一个让值产生变化的例子。如果这类错误后,导致被改变的值,并不是指针的值,则在这么小的程序中,系统不一定都崩溃,它不过是让部分你没照顾到的地方变了变值,却没有影响输出。

  建议楼主阅读一下TCPL有关临时变量一节,看看各种条件下生成的临时变量的作用域,与给出名字的局部变量间,有何差同。

  三、其他一些为什么的例子

  关于C++的为什么特别多,如果你不是经验丰富且善于思考,是很难理解为什么有这么多为什么的。当然,为什么的多少,是个程度问题,有差异存在的地方就有程度问题,不同的人善用不同的东西,C++是“小众”的,但还不至于只是几个人的,毕竟TIOBE还排第3。

  1.operator重载的解析顺序为什么如现在标准那样设计?是权衡了使用者的方便,和编译器的效率之间的一种平衡,它过度自由带来的是呈指数级上升的编译时开销,且该开销并不一定值得。

  2.内置数组,为什么不设置下标检测?如果检测下标,定然就会在每次访问下标时,做是否越界的检验,这就带来了运行时开销。如果你的算法非常好,定然不需要检测下标,则语言假定一定要在每次访问下标时都判断,就会影响效率并失去选择的机会。如果设置N个选项,可以用来关闭或打开是否检测下标,那不应该是一种语言应该干的,各有各的侧重点。

  3.C语言传数组参数为什么默认是转换成指针类型?以C语言产生那个年代的硬件条件,复制数组很奢侈,尤其函数被调用往往很频繁,算法要尽量往不复制的情况下设计,如果实在必要,非要复制,你也可以手动memcpy嘛!总之它不是默认项。C++给了用户另一种选项,即通过加上引用,而使得能够真正传整个数组,不过这都是很多年以后的事了。

  4.for语句为什么有的灵活有的严格?像在Ada中的语法,便是禁止循环变量被改变,且不能设置步长值,要想达到这两个目的,便只能用其他变量再过渡,这样做是为了高度的安全。反之,C语言的for则非常灵活,也没有Ada那么多的限制,但这种灵活并不能保证用户用其写出错误逻辑的代码;VB的自由度则介于二者之间。不能因为这些语言的设计不同,而指责其中某一种语言为何不对某一语法特性做必要的限制,它真的必要吗?个案好说,但综合全局,很难评估。

  四、设计者们不傻

  且任何有影响力的技术,其规范,都是经过全球大量从业者多年实践后,总结整理并论证出来的,并不是一个或几个人拍拍脑袋就草率决定的,因此C++的新标准化过程要历时8年之久。Bjarne Stroustrup不傻,Herb Sutter, Stanley Lippman, ScottMeyer, Alexander Stepanov, Andrew Koenig等人也不傻,标准委员会都不是白给的,大多数细节问题早就被提出过。具体实现,要难得多,这点语法层面上的皮毛问题,都不值一提。

注:转载出自:http://blog.csdn.net/d04421024/article/details/7317456

阅读(1) | 评论(0) | 转发(0) |
0

上一篇:c++ try_catch throw

下一篇:C++中inline函数

评论热议
  • 6
    点赞
  • 3
    收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:撸撸猫 设计师:马嘣嘣 返回首页
评论 2
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值