那谁的技术博客

感兴趣领域:高性能服务器编程,存储,算法,Linux内核
随笔 - 210, 文章 - 0, 评论 - 1183, 引用 - 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 那谁 阅读(22307) 评论(8)  编辑 收藏 引用 所属分类: C\C++

评论

# re: 一个关于临时对象和虚拟析构函数的问题  回复  更多评论   

base* p1=&derived();
后,p1指向的对象已被销毁了,p1->???行为是未定义的

实际上并不需要virtual ~base(),只要定义~base()就有上述效果了,编译器会为这个析构加上纠正vtbl的功能,相比之下,编译器赠送的析构函数却不会纠正vtbl
2006-02-16 23:57 | control

# re: 一个关于临时对象和虚拟析构函数的问题  回复  更多评论   

base* p1=&derived();
这个语句执行完毕,临时对象已经不存在了。

觉得楼主的解释不合理,但是我也拿不出好的解释。
2007-05-18 20:31 | kingsun555

# re: 一个关于临时对象和虚拟析构函数的问题[未登录]  回复  更多评论   

@control
同意你的观点。
博主曾说自己的C/C++水平足以应付工作。然而,我不得不说,有很多理解是错的,表面看起来可以工作,且例子也能论证你的观点,那只不过是碰巧了而已。而且总是以C的观点来看待C++,这是很危险的。
2008-06-15 20:26 | raof01

# re: 一个关于临时对象和虚拟析构函数的问题[未登录]  回复  更多评论   

刚才吃西瓜时突然想到:base* p1=&derived(); 后,临时对象已不复存在,然而编译器依然会将该临时对象的内存区解析为base,然而vtbl中的内容依然指向derived::Print(),为base加上析构,这样vtbl先是指向derived::Print(),而后调用~base()时,该临时对象的类型变成了base,vtbl表项被纠正为指向base::Print()。
2008-06-15 21:27 | raof01

# re: 一个关于临时对象和虚拟析构函数的问题  回复  更多评论   

未定义行为,
base* p1指向一个已经被析构了的对象。

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

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

别老那java那一套。
2008-09-12 13:14 | trim

# re: 一个关于临时对象和虚拟析构函数的问题  回复  更多评论   

不论是否加入虚拟析构函数,临时对象在base* p1=&derived();之后都已经析构。
derived对象的内存分布:base类对象,然后是derived类的虚函数表和成员变量等(不同编译器,虚函数表的位置可能不同)
析构后derived的虚表指针重置,在调用p1->print()时,只能调用base类的print()。
这种调用存在很大隐患,如果在调用p1->print()前,其他操作改变了那快内存就可能出现其他结果。
2010-01-29 00:52 | wcj

# re: 一个关于临时对象和虚拟析构函数的问题  回复  更多评论   

很汗颜!!!!
2010-04-23 12:44 | 小时候可靓了

# re: 一个关于临时对象和虚拟析构函数的问题[未登录]  回复  更多评论   

懂不懂的啊
2010-07-29 08:51 | haha

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理