一个关于ctor和dtor的问题:
有如下一段程序,测试环境:VC.net 2003,关闭所有优化
struct A
{
~A() { printf("~A "); }
};
struct B : public A
{
~B() { printf("~B "); }
};
void main()
{
{
const A & a = B();
}
}
我认为这段程序应当只产生一个B的实例,
而程序的输出却是:~B ~A ~B ~A
也就是说A和B的析构函数各被调用了两次,猜想是由于临时变量引起的。
但当我给A或者B增加一个缺省构造函数,比如:A() {} 或者 B(){}后,
输出结果就变成了:~B ~A
请问各位C++牛人,能否解释这个现象?是VC的bug还是C++语言本身所致?
问题点数:100、回复次数:19Top
1 楼sevecol(sevecol.blogone.net)回复于 2003-09-01 22:02:03 得分 40
关注,他的编译器的行为呢?
Top
2 楼oopig(面向对象的猪)回复于 2003-09-01 22:05:01 得分 40
vc6.0和vc7有这个现象,dev-c++4.9.8.0和g++3.2没有这个现象。和继承也没有关系。
而且如果在vc中把语句外面的那一对花括号去掉,则只创建一个临时对象,否则创建两个临时对象。
我倾向于认为这是vc的一个“特性”。还是要翻翻《c++程序语言设计》或者c++标准才能下结论。
Top
3 楼oopig(面向对象的猪)回复于 2003-09-01 22:15:37 得分 0
不好意思,搞错了。
不论是否去掉多出来的那对花括号,vc都是创建两个对象。bcb5.0的行为和vc一致。
Top
4 楼oopig(面向对象的猪)回复于 2003-09-01 23:10:35 得分 0
变形代码:
{
B a;
const B &b = &a;
}
以上代码的输出为:~B。
const A & a = B();这行代码大概可以这么理解:
1。B()这个临时对象的生命期只存在于这个表达式
2。常量引用b的生命期则存在于两个花括号之间
3。为了解决1和2的矛盾,有两种解决办法:创建两个变量或者实际上延长B()临时变量的生命周期。看来vc和bcb选择了前者而g++选择了后者。至于那种方法符合c++标准,又或者c++标准对此没有规定,需要继续考证。Top
5 楼svenwang(svenwang)回复于 2003-09-01 23:34:55 得分 0
upTop
6 楼oopig(面向对象的猪)回复于 2003-09-01 23:38:18 得分 0
《c++程序设计语言》p227有句话:
除非一个临时对象被约束到某个引用,或者被用于做命名对象的初始化,否则它将总在建立它的那个完整表达式结束时销毁。所谓完整表达式就是那种不是其他表达式的子表达式的表达式。
按照我对这句话的理解,vc和bcb的做法不符合c++标准。
Top
7 楼chenlee()回复于 2003-09-02 10:52:27 得分 0
我当初用内层的花括号只是为了限定a的生存期,方便调试。
不管一对还是两对,程序的结果都一样。
Top
8 楼sevecol(sevecol.blogone.net)回复于 2003-09-02 16:33:28 得分 0
我认为这个符合C++标准的。
首先这些临时对象都是按照C++标准所规定生存周期存活的。在离开了{},那个邦定到引用的临时对象析构。
第二,在C++标准中没有提到临时对象产生索要必须遵循规则(我没找到),我觉得这是编译器优化的选择。Top
9 楼oopig(面向对象的猪)回复于 2003-09-02 16:57:54 得分 0
sevecol(看什么看...):那你对这句话怎么理解?
除非一个临时对象被约束到某个引用,或者被用于做命名对象的初始化,否则它将总在建立它的那个完整表达式结束时销毁。所谓完整表达式就是那种不是其他表达式的子表达式的表达式。
Top
10 楼sevecol(sevecol.blogone.net)回复于 2003-09-02 17:27:22 得分 0
const A & a = B();
这个就是临时对象绑定到引用上,这个临时对象只有到了被初始化的引用的生命周期结束时被销毁,也就是{}的最后被销毁。
这个临时对象不管是在你有没有构造函数的时候都被正确的处理。
关键是VC创建的另一个临时对象,这个临时对象,在这个全的表达式后销毁。而C++标准对于编译器如何产生临时对象好像没有具体的规定,只是规定了临时对象的生命周期和语义的表达一致。
我认为对于没有构造函数的情况下,VC产生两个临时变量也是符合C++标准的。
同样的有构造函数情况下(可能会引发某种优化),产生一个临时对象也是符合C++标准的。
当然上面成立的前提是所产生的临时对象满足C++标准对于临时对象生存周期的约束。Top
11 楼plainsong(短歌)()回复于 2003-09-02 18:36:25 得分 10
BCB6中:
输出为~A ~B ~A,大概是A和B各有一个对象,发生了切片。
而VC6中:
~B ~A ~B ~A,大概是有两个B对象产生。
当把A的析构改为virtual时,全都正常(输出为~B~A)。
问题是在基类析构非多态时子类的向上映射真的有意义吗?Top
12 楼oopig(面向对象的猪)回复于 2003-09-02 18:48:23 得分 0
>>而C++标准对于编译器如何产生临时对象好像没有具体的规定,只是规定了临时对象的生命周期和语义的表达一致。
所谓临时变量我认为就是在完整表达式中建立的匿名对象,如果不是这样那就成了局部变量了。结合《c++程序设计语言》上的描述,我认为vc的这种行为是不符合c++标准的。Top
13 楼oopig(面向对象的猪)回复于 2003-09-02 18:50:11 得分 0
plainsong(短歌):
我试了VC下const B &b = B();这个语句还是创建了两个对象,所以和基类析构非多态时子类的向上映射没有什么关系。
Top
14 楼sevecol(sevecol.blogone.net)回复于 2003-09-02 18:58:11 得分 0
to oopig(面向对象的猪):
C++标准中好象没有规定产生多少个临时对象吧?只要产生的临时对象满足临时对象的生命周期的约束,我认为就没有不符和C++的标准.
Top
15 楼oopig(面向对象的猪)回复于 2003-09-02 19:07:39 得分 0
sevecol(如果问题解决,就请结贴):
那么B();这个语句产生两个或者三个或者n个对象都不算是不符合c++标准了?Top
16 楼sevecol(sevecol.blogone.net)回复于 2003-09-02 19:14:48 得分 0
我记得在Inside The C++ Object Model中文版里面的关于临时对象有说
C++ Standard允许编译器对于临时对象的产生有完全的自由度Top
17 楼chenlee()回复于 2003-09-02 20:09:38 得分 0
在C++ Standard 8.5.3一节中找到了关于引用时临时变量的详细说明:
=============================================================================
If the initializer expression is an rvalue, with T2 a class type,
and “cv1 T1” is referencecompatible with “cv2 T2,” the reference
is bound in one of the following ways (the choice is implementationdefined) :
- The reference is bound to the object represented by the rvalue
(see 3.10) or to a subobject within that object.
- A temporary of type “cv1 T2” [sic] is created, and a constructor is
called to copy the entire rvalue object into the temporary. The reference
is bound to the temporary or to a subobject within the temporary.
The constructor that would be used to make the copy shall be callable whether
or not the copy is actually done.
[Example:
struct A { };
struct B : public A { } b;
extern B f();
const A& rca = f(); // Either bound to the A subobject of the B rvalue,
// or the entire B object is copied and the reference
// is bound to the A subobject of the copy
—end example]
=============================================================================
由上可知,const A & a = B();
如果采用第一种方法,则只有一个临时变量;
而采用第二种方法,则有两个临时变量。
由此可见VC并没有违反C++的规定。
各位看我的理解是否正确?
Top
18 楼xueweizhong(薛卫忠)回复于 2003-09-04 10:38:41 得分 10
to plainsong(短歌):
>BCB6中:
> 输出为~A ~B ~A,大概是A和B各有一个对象,发生了切片
1)我特意仔细核对了一下,CBUILDER在
当且仅当 类A或类B拥有trivial-ctor时 出现你说的这种情况。
2)其他情况下表现为和g++,CC同样的情况。
3)很明显,
在A或B拥有trivial-ctor时的BORLAND的行为不符合C++98标准,
应该算是一个BORLAND COMPILER的一个BUG。
Top
19 楼chenlee()回复于 2003-09-05 09:47:59 得分 0
看来就是这样了。
结贴!!Top




