- 浏览: 429418 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
su6838354:
我有点疑问啊,thread1中的i自增的慢的话,thread2 ...
浅析pthread_cond_wait -
zeronever:
请问pthread_cond_signal有解锁操纵吗?我在p ...
浅析pthread_cond_wait -
paladin1988:
你这帖子真心不错。。
浅谈bitmap算法 -
parabellum_sky:
昨天还有个姑娘让我去考我说会考虑
个人日志
虚析构函数
一 、为何要单独讨论虚析构函数?虚函数在类中到底有什么作用?
看如下代码:
#include <iostream> using namespace std; class enemytarget { public: enemytarget() { cout << "Call the Base Constructor!\n"; ++numtargets; cout << "numtargets :" << numtargets << endl; } enemytarget(const enemytarget&) { cout << "Call the Base Copy Constructor!\n"; ++numtargets; cout << "numtargets :" << numtargets << endl; } //virtual ~enemytarget() ~enemytarget() { cout << "Call the Base Destructor!\n"; --numtargets; cout << "numtargets :" << numtargets << endl; } static size_t numberoftargets() { return numtargets; } virtual bool destroy(); // 摧毁enemytarget对象后 // 返回成功 private: static size_t numtargets; // 对象计数器 }; bool enemytarget::destroy() { if(numtargets == 0) return true; } // 类的静态成员要在类外定义; // 缺省初始化为0 size_t enemytarget::numtargets; class enemytank: public enemytarget { public: enemytank() { cout << "Call the Inherit Constructor!\n"; ++numtanks; cout << "numbertanks :" << numtanks << endl; } enemytank(const enemytank& rhs) : enemytarget(rhs) { cout << "Call the Inherit Copy Constructor!\n"; ++numtanks; cout << "numbertanks :" << numtanks << endl; } ~enemytank() { cout << "Call the Inherit Destructor!\n"; --numtanks; cout << "numbertanks :" << numtanks << endl; } static size_t numberoftanks() { return numtanks; } virtual bool destroy(); private: static size_t numtanks; // 坦克对象计数器 }; bool enemytank::destroy() { if(numtanks == 0) return true; } size_t enemytank::numtanks; int main() { enemytarget *targetptr = new enemytank[10]; //enemytank *targetptr = new enemytank[10]; delete [] targetptr; return 0; }
输出结果为:
Call the Base Constructor!
numtargets :1
Call the Inherit Constructor!
numbertanks :1
Call the Base Constructor!
numtargets :2
Call the Inherit Constructor!
numbertanks :2
Call the Base Constructor!
numtargets :3
Call the Inherit Constructor!
numbertanks :3
Call the Base Constructor!
numtargets :4
Call the Inherit Constructor!
numbertanks :4
Call the Base Constructor!
numtargets :5
Call the Inherit Constructor!
numbertanks :5
Call the Base Constructor!
numtargets :6
Call the Inherit Constructor!
numbertanks :6
Call the Base Constructor!
numtargets :7
Call the Inherit Constructor!
numbertanks :7
Call the Base Constructor!
numtargets :8
Call the Inherit Constructor!
numbertanks :8
Call the Base Constructor!
numtargets :9
Call the Inherit Constructor!
numbertanks :9
Call the Base Constructor!
numtargets :10
Call the Inherit Constructor!
numbertanks :10
Call the Base Destructor!
numtargets :9
Call the Base Destructor!
numtargets :8
Call the Base Destructor!
numtargets :7
Call the Base Destructor!
numtargets :6
Call the Base Destructor!
numtargets :5
Call the Base Destructor!
numtargets :4
Call the Base Destructor!
numtargets :3
Call the Base Destructor!
numtargets :2
Call the Base Destructor!
numtargets :1
Call the Base Destructor!
numtargets :0
可以看出子类的析构函数没有被调用,为何没有被调用?
enemytarget *targetptr = new enemytank[10];targetptr 是基类的对象,当我们用基类的对象去delete派生类对象时会出现此类问题。当通过基类的指针去删除派生类的对象,而基类又没有虚析构函数时,结果将是不可确定的。为了避免这个问题,只需要使enemytarget的析构函数为virtual。
如果把基类修改如下:
virtual ~enemytarget() { cout << "Call the Base Destructor!\n"; --numtargets; cout << "numtargets :" << numtargets << endl; }
输出结果为:
Call the Base Constructor!
numtargets :1
Call the Inherit Constructor!
numbertanks :1
Call the Base Constructor!
numtargets :2
Call the Inherit Constructor!
numbertanks :2
Call the Base Constructor!
numtargets :3
Call the Inherit Constructor!
numbertanks :3
Call the Base Constructor!
numtargets :4
Call the Inherit Constructor!
numbertanks :4
Call the Base Constructor!
numtargets :5
Call the Inherit Constructor!
numbertanks :5
Call the Base Constructor!
numtargets :6
Call the Inherit Constructor!
numbertanks :6
Call the Base Constructor!
numtargets :7
Call the Inherit Constructor!
numbertanks :7
Call the Base Constructor!
numtargets :8
Call the Inherit Constructor!
numbertanks :8
Call the Base Constructor!
numtargets :9
Call the Inherit Constructor!
numbertanks :9
Call the Base Constructor!
numtargets :10
Call the Inherit Constructor!
numbertanks :10
Call the Inherit Destructor!
numbertanks :9
Call the Base Destructor!
numtargets :9
Call the Inherit Destructor!
numbertanks :8
Call the Base Destructor!
numtargets :8
Call the Inherit Destructor!
numbertanks :7
Call the Base Destructor!
numtargets :7
Call the Inherit Destructor!
numbertanks :6
Call the Base Destructor!
numtargets :6
Call the Inherit Destructor!
numbertanks :5
Call the Base Destructor!
numtargets :5
Call the Inherit Destructor!
numbertanks :4
Call the Base Destructor!
numtargets :4
Call the Inherit Destructor!
numbertanks :3
Call the Base Destructor!
numtargets :3
Call the Inherit Destructor!
numbertanks :2
Call the Base Destructor!
numtargets :2
Call the Inherit Destructor!
numbertanks :1
Call the Base Destructor!
numtargets :1
Call the Inherit Destructor!
numbertanks :0
Call the Base Destructor!
numtargets :0
这种情况就避免了派生类析构函数不被调用的错误。
二、什么情况下声明析构函数为虚函数,什么情况下不用声明为虚函数?
如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。
看下面的例子:
// 一个表示2d点的类
class point {
public:
point(short int xcoord, short int ycoord);
~point();
private:
short int x, y;
};
如果一个short int占16位,一个point对象将刚好适合放进一个32位的寄存器中。另外,一个point对象可以作为一个32位的数据传给用c或fortran等其他语言写的函数中。但如果point的析构函数为虚,情况就会改变。
实现虚函数需要对象附带一些额外信息,以使对象在运行时可以确定该调用哪个虚函数。对大多数编译器来说,这个额外信息的具体形式是一个称为vptr(虚函数表指针)的指针。vptr指向的是一个称为vtbl(虚函数表)的函数指针数组。每个有虚函数的类都附带有一个vtbl。当对一个对象的某个虚函数进行请求调用时,实际被调用的函数是根据指向vtbl的vptr在vtbl里找到相应的函数指针来确定的。
虚函数实现的细节不重要(当然,如果你感兴趣,可以阅读条款m24),重要的是,如果point类包含一个虚函数,它的对象的体积将不知不觉地翻番,从2个16位的short变成了2个16位的short加上一个32位的vptr!point对象再也不能放到一个32位寄存器中去了。而且,c++中的point对象看起来再也不具有和其他语言如c中声明的那样相同的结构了,因为这些语言里没有vptr。所以,用其他语言写的函数来传递point也不再可能了,除非专门去为它们设计vptr,而这本身是实现的细节,会导致代码无法移植。
所以基本的一条是,无故的声明虚析构函数和永远不去声明一样是错误的。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析构函数。
三、当类里没有虚函数的时候,也会带来非虚析构函数问题。
例子如下:
template<class t> // 基类模板 class array { // (来自条款13) public: array(int lowbound, int highbound); ~array(); private: vector<t> data; size_t size; int lbound, hbound; }; template<class t> class namedarray: public array<t> { public: namedarray(int lowbound, int highbound, const string& name); ... private: string arrayname; };
如果在应用程序的某个地方你将指向namedarray类型的指针转换成了array类型的指针,然后用delete来删除array指针,那你就会立即掉进“不确定行为”的陷阱中。
int main() { namedarray<int> *pna = new namedarray<int>(10, 20, "impending doom"); array<int> *pa; pa = pna; // namedarray<int>* -> array<int>* delete pa; // 不确定! 实际中,pa->arrayname // 会造成泄漏,因为*pa的namedarray // 永远不会被删除 }
现实中,这种情形出现得比你想象的要频繁。让一个现有的类做些什么事,然后从它派生一个类做和它相同的事,再加上一些特殊的功能,这在现实中不是不常见。namedarray没有重定义array的任何行为——它继承了array的所有功能而没有进行任何修改——它只是增加了一些额外的功能。但非虚析构函数的问题依然存在.
所以一个类被定义为基类要注意虚析构函数的声明。
发表评论
-
Google编程风格
2012-04-01 17:05 1071Google编程风格(自己整 ... -
VS2008快捷键的设置
2012-02-02 13:43 2609VS2008快捷键的设置 VS2008默认的快捷键和VC++ ... -
LINK : fatal error LNK1000: Internal error during IncrBuildImage
2011-12-16 17:07 1184Win7安vc2008编译报LINK : fatal erro ... -
volatile关键字(摘自:百度百科)
2011-12-16 14:59 699volatile关键字[align=center][/alig ... -
InterlockedIncrement
2011-12-16 14:44 2143InterlockedIncrement[align=cent ... -
关键词explicit
2011-12-10 20:32 822关键词explicit[size=large][/size][ ... -
Souce Insight 设置
2011-12-09 17:16 1951Souce Insight 设置 【问题】 Source I ... -
VS2008下Boost库的安装编译下载boost库
2011-11-09 19:19 2009下载boost库 (最好去官网下,一般有SGI(GCC+用的较 ... -
C++多态技术的实现和反思(转)
2011-10-17 17:05 772面向对象技术最早出现于1960年代的Simula 67系统,并 ... -
memmove and memcpy
2011-07-31 13:11 1148memmove and memcpy 字符串的拷贝函数mem ... -
Polymorphism & Virtual Function
2011-07-25 21:38 826Polymorphism & Virtual Func ... -
C++ 不要重新定义继承的非虚函数
2011-07-18 14:15 1266不要重新定义继承的非虚函数 如果基类和派生类有相同的非虚函数 ... -
C++ 虚函数表解析
2011-07-14 21:02 943C++ 虚函数表解析 为什么在C++机制里要有虚函数表?虚函 ... -
C++ 默认构造函数
2011-07-14 11:39 3689C++ 默认构造函数 一直 ... -
尽量使用const
2011-07-12 10:51 1078尽可能的使用const const: ... -
Operator=
2011-07-11 21:54 909Operator= 赋值构造函数 ... -
初始化列表和声明顺序之间的关系
2011-07-11 09:18 1439初始化列表和声明的顺序之间的关系 类中数据成员的声明顺序和初 ... -
初始化函数列表和构造函数内赋值之区别
2011-07-10 17:05 4467初始化函数列表和构造 ... -
传值和传引用的区别
2011-07-10 15:30 2346传值和传引用的区别 在C语言中,大都是通过值传递,C++也是 ... -
构造函数,析构函数和赋值操作符
2011-07-10 14:16 1195构造函数,析构函数和 ...
相关推荐
虚析构函数示例 c++析构函数是否是虚函数时的差别,敬请留意
该资源的内容主要是 虚基类 虚函数成员 虚析构函数的具体的区别
我们知道,用C++开发的时候,用来做基类的类的析构函数一般都是虚函数。可是,为什么要这样做呢?下面用一个小例子来说明:
详细解释了为什么析构函数可以是虚函数,而构造函数不能是虚函数
C++中基类的析构函数为什么要用virtual虚析构函数.pdf
虚析构函数 析构函数的工作方式是:底层的派生类(most derived class)的析构函数先被调用,然后调用每一个基类的析构函数。 因为在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非...
c++ virtual 虚析构函数及虚函数的详细例子.rar
C_虚构造函数和虚析构函数.pdf
本文给大家介绍了C++中确定基类有虚析构函数的方法。
//析构函数做成员函数 }; Base::~Base()//成员函数实现 { cout; } class Derived:public Base { public: Derived(); ~Derived(); private: int *p; }; Derived::Derived() { p=new int(0);//从堆上分配一个int型...
主要介绍了C++中虚析构函数的作用及其原理分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
构造函数不能声明为虚函数,析构函数可以声明为虚函数。
本篇文章是对C++中虚析构函数的作用进行了详细的分析介绍,需要的朋友参考下
C++析构函数使用virtual的原因
在C++中,不能声明虚构造函数,但可以声明虚析构函数。多态性是指不同的对象对同一消息有不同的行为特性。虚函数作为运行时多态性的基础,主要是针对对象的,而构造函数是在对象产生之前运行的,因此虚构造函数是没有...
本文给大家分享了避免析构函数调用虚函数。
C++中析构函数定义成虚函数的原因 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/SearchLife/archive/2009/03/12/3985341.aspx
C++静态关联与动态关联、C++是怎样实现多态性的 在现实生活中,多态性的例子是很多的。我们分析一下人是怎样处理多 态性的。例如,新生被录取人大学,在人学报到时,先有一名工作人员审查材料,他的职责是甄别资格,...
1、原因: 在实现多态时, 当用基类指针操作派生类, 在析构时候防止只析构基类而不析构派生类。 2、例子: (1)、 #include using namespace std; class Base{ public: Base() {};...