随笔-174  评论-598  文章-0  trackbacks-0
在CU逛,别人问到这样的一个问题,大致如下:

#include <iostream>
#include 
<string>
using namespace std;
class base
{
        
public:
                
virtual void print(){cout<<"base::print()"<<endl;}
                
//virtual ~base(){}//增加虚析构函数,将导致输出不一致。
}
;
class derived :public base
{
        
public:
                
virtual void print(){cout<<"devrived::print()"<<endl;}
}
;
int main(int argc, char *argv[])
{
        
base* p1=&derived();
        p1
->print();

        derived d;
        
base* p2=&d;
        p2
->print();

        
return 0;
}


加入虚拟析构函数后,输出为:
base::print()
devrived::print()
不加入虚拟析构函数,输出则为:
devrived::print()
devrived::print()



我的解释如下:
实际上,第一个赋值指向的是一个临时对象,加入与没有加入虚拟析构函数的区别在于:加入之后,在derived类中会合成一个析构函数以便调用base的虚拟析构函数,如果没有加入的话那么就不会合成这个析构函数,所有的资源在main函数结束之后才回收.

因此,对于代码: base* p1=&derived();
没有加入虚拟析构函数的时候因为derived()函数生成的临时对象没有被销毁,因此对它的调用是对derived的调用;当加入虚拟析构函数之后,derived()函数生成的临时对象在以上的赋值完成之后就会调用析构函数进行析构,这个时候再次对p1调用print函数时,因为临时对象已经析构,那么这个调用就是对base的调用了.

我做了一个实验的代码,加了一些东西,大家看看~~

#include <iostream>
#include 
<string>
using namespace std;
class base
{
        
public:
                
virtual void print(){cout<<"base::print()"<<endl;}
                
virtual ~base(){cout << "~base()\n";}//增加虚析构函数,将导致输出不一致。
}
;
class derived :public base
{
        
public:
                
virtual void print(){cout<<"devrived::print()"<<endl;}
                
//virtual ~derived(){cout << "~derived()\n";}//增加虚析构函数,将导致输出不一致。
}
;

int main(int argc, char *argv[])
{
        derived 
*= &derived();
        
base* p1 = t;
        p1
->print();

        derived d;
        
base* p2=&d;
        p2
->print();

        
// 第一部分相当于以下的代码
        
// 用temp模拟临时对象
        derived temp;
        derived 
*= &temp;
        
base *p3 = s;
        
// 这次调用是对derived的函数调用
        p3->print();
        
// 显式地对temp进行析构
        temp.base::~base();
        
// 析构完后,同样的一个内存,却是用base指针去解释,
        
// 因此调用的是base的函数
        p3->print();

        
return 0;
}
 
posted on 2006-02-16 23:24 那谁 阅读(2059) 评论(5)  编辑 收藏 引用 所属分类: C\C++

评论:
# re: 一个关于临时对象和虚拟析构函数的问题 2006-02-16 23:57 | control
base* p1=&derived();
后,p1指向的对象已被销毁了,p1->???行为是未定义的

实际上并不需要virtual ~base(),只要定义~base()就有上述效果了,编译器会为这个析构加上纠正vtbl的功能,相比之下,编译器赠送的析构函数却不会纠正vtbl  回复  更多评论
  
# re: 一个关于临时对象和虚拟析构函数的问题 2007-05-18 20:31 | kingsun555
base* p1=&derived();
这个语句执行完毕,临时对象已经不存在了。

觉得楼主的解释不合理,但是我也拿不出好的解释。  回复  更多评论
  
# re: 一个关于临时对象和虚拟析构函数的问题[未登录] 2008-06-15 20:26 | raof01
@control
同意你的观点。
博主曾说自己的C/C++水平足以应付工作。然而,我不得不说,有很多理解是错的,表面看起来可以工作,且例子也能论证你的观点,那只不过是碰巧了而已。而且总是以C的观点来看待C++,这是很危险的。  回复  更多评论
  
# re: 一个关于临时对象和虚拟析构函数的问题[未登录] 2008-06-15 21:27 | raof01
刚才吃西瓜时突然想到:base* p1=&derived(); 后,临时对象已不复存在,然而编译器依然会将该临时对象的内存区解析为base,然而vtbl中的内容依然指向derived::Print(),为base加上析构,这样vtbl先是指向derived::Print(),而后调用~base()时,该临时对象的类型变成了base,vtbl表项被纠正为指向base::Print()。  回复  更多评论
  
# re: 一个关于临时对象和虚拟析构函数的问题 2008-09-12 13:14 | trim
未定义行为,
base* p1指向一个已经被析构了的对象。

即使如楼上所说,那也是暂时的运行结果,那时因为编译器内部实现(vtbl)遗留下的残骸。

写这样的程序,
[1] 代价高(临时对象的构造和析构)
[2] bt

别老那java那一套。  回复  更多评论
  


标题  
姓名  
主页
验证码 *
内容(提交失败后,可以通过“恢复上次提交”恢复刚刚提交的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
[使用Ctrl+Enter键可以直接提交]
.NET频道  博客园社区  闪存
网站导航: