分享
 
 
 

一个关于临时对象的BUG(上)

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

一个关于临时对象的BUG

译注:由WDN 2003年6月的BUG++翻译,有删减。

我相信任何一个使用C++超过一定时间的程序员都不会否认这样一个事实:使用C++需要有足够的技巧。它充满了有各种各样的难以识别的陷阱,顷刻就可以让一段看起来毫无破绽的代码崩溃。例如,对C/C++的新手而言,学会如何考虑对象的生存期就是他们必须跨越的一个障碍,这方面最典型的问题,就是对对象指针的使用,特别是在使用一个已经被删除了的对象指针的时候:

MyClass *mc = new MyClass;

// Do some stuff

delete mc;

mc->a = 1; // Uh oh...mc is no longer valid!

一些更玄妙的事情发生在函数返回的时候,我们假设一个函数,例如foo()返回一个MyClass类型的对象引用:

MyClass &foo()

{

MyClass mc;

// Do some things

return mc;

}

这段有问题的代码实际上是完全合法的,当函数foo()的生存期还没有结束的时候,mc就会被销毁掉,但函数返回的是它的一个引用,这样一来,函数的调用者将得到一个引用,它指向一个已经不存在对象,如果你运气够好,你也许可以得到一个来自编译器的警告(例如VC 7.0将给出这样一个警告:“warning C4172: returning address of local or temporary.”),但注意不是每种编译器都会这么友好。

这是一个很常见的例子,我相信每个C++程序员都至少犯过一次这样的错误。然而,对于C++的临时对象而言,事情变得稍微有点复杂。如果我将foo的定义稍稍变一下,让它返回一个对象的拷贝,而不是引用,会发生什么情况?

MyClass foo()

{

MyClass mc;

return mc;

}

现在,当foo返回的时候,它将生成一个临时对象,这个临时对象将被赋给调用函数指定的一个变量。为了看看这一切是如何发生的,我们看看Listing 1代码的执行结果:

Default constructor

Copy constructor

Destructor

Returned from foo

Destructor

Listing 1 Function returning a temporary object

// Demonstrates returning a temporary object.

#include <iostream>

using namespace std;

class MyClass

{

public:

MyClass(const MyClass &)

{ cout << "Copy constructor\n"; }

MyClass()

{ cout << "Default constructor\n"; }

MyClass &operator=(const MyClass &)

{

cout << "Assignment operator\n";

return *this;

}

MyClass::~MyClass()

{

cout << "Destructor\n";

}

};

MyClass foo()

{

MyClass mc;

// Return a copy of mc.

return mc;

}

int main()

{

// This code generates the temporary

// object directly in the location

// of retval;

MyClass rv1 = foo();

// This code generates a temporary

// object, which then is copied

// into rv2 using the assignment

// operator.

//MyClass rv2;

//rv2 = foo();

cout << "Returned from foo\n";

return 0;

}

你也许会想,这里是不是有一个对象丢失了,毕竟,如果当你看了伪代码以后,你会认为这样一些事情是应该发生的:

在foo中,mc被声明了,它调用了缺省的构造函数,然后,foo返回了一个临时对象,这个临时对象是对mc的拷贝,并因此调用了拷贝构造函数,这个临时对象被赋值给了rv1,并再次调用拷贝构造函数。

但是请等一下,我们查看应用程序的输出,拷贝构造函数却只被调用了一次!而本来应该有三个对象生成:mc(在foo函数中),一个临时对象,以及rv1。为什么不是调用三次构造函数?这个问题的答案就是:C++标准所允许的代码优化欺骗了我们,这样做的目的是为了避免代码过于低效,如果一个临时对象作为返回值被立即赋给另一个对象,这个临时对象本身将被构造到被赋值对象在内存中的位置。这样避免了一次无谓的构造函数调用,当构造函数需要做很多初始化工作的话,这样可以节省不少时间(如果你对这方面的内容很感兴趣,请参考C++标准的第12.2节,第3段)。

另外有一个相关的例子,例如,当rv已经被声明了:

MyClass rv;

rv = foo();

这时候,临时对象将不被构造到rv的位置,因为发生foo调用的时候,rv已经被构造过了,因此,这个临时的返回值必须被做为一个独立的对象来构造,然后再赋值给rv。实际上,如果你将Listing 1代码中的注释打开,你将会得到这样一些期待的结果(括号中的注释是我加上的):

Default constructor (rv2)

Default constructor (mc)

Copy constructor (temporary)

Destructor (mc)

Assignment operator (rv2 = temporary)

Destructor (temporary)

Returned from foo

Destructor (rv2)

这里需要注意的是临时对象在它被生成的表达式执行结束的时候被销毁,换句话说,析构函数将在这句话执行的末尾被调用:

rv = foo(); // Temporary is destroyed here

(未完待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有