首页 > 代码库 > 《COM原理与应用》题外话——C++虚函数表和delete this

《COM原理与应用》题外话——C++虚函数表和delete this

     delete this看起来非常的奇怪,我记得在《C++ Primer》中提到过delete this,但是我已经忘了在哪了,也一直没有找到(因为没有电子版,所以一直没找到~)。《C++ Primer》中提到的是在析构函数中使用delete this会造成析构函数的无限调用,最终造成栈溢出。我也在网上看了一些,很多人觉得不该使用delete this,因为会引起一些问题。但是delete this也挺有用处的,就和goto语句一样,不应该被一棍子打死(goto语句其实怪好用的:-D)。在COM组件的运用中,有很多地方用到了delete this。在C++的智能指针的模板的实现中,也用到了delete this。所以今天我就讲讲我自己对于delete this的理解。
  要delete this,首先得搞清楚delete是怎么工作的。这里有一段MSDN的话:
  对于不是类类型(class、struct 或 union)的对象,将调用全局 delete 运算符。 对于类类型的对象,如果删除表达式以一元范围解析运算符 (::) 开始,则会在全局范围中解析释放函数的名称。  否则,delete 运算符将在释放内存之前为对象调用析构函数(如果指针不为 null)。  可为每个类定义 delete 运算符;如果给定类不存在这种定义,则会调用全局 delete 运算符。  如果删除表达式用于释放其静态对象具有虚拟析构函数的类对象,则将通过对象的动态类型的虚拟析构函数解析释放函数。 (链接如下:http://msdn.microsoft.com/zh-cn/library/z5308zac.aspx)
  第二句话我没看懂,不过其他的还是看懂了。总的来说,delete运算符会先调用析构函数,然后才释放指针所指向的内存。所以delete后面跟的指针一定要指向动态分配的内存,也就是说一定要是new出来的内存。既然是释放内存,当然我们得看看一个函数对象里面到底存储了一些什么东西,那样我们才知道删除了一些什么东西。
一、虚函数表
  C++的对象中存储的不仅仅只有该对象的成员变量,还有虚函数的指针。然后虚函数的指针组合起来就是虚函数表了。虚函数表中的每一项都指向了虚函数的地址。正式通过这种方式,C++实现了指针或者引用的动态绑定(或者说实现了多态吧~)。如果没有这张表,C++中申明一个基类的指针,而实际上指针指向的是子类,如果调用这个对象的一个虚函数,那么就不会调用子类的成员函数而是调用父类的成员函数。在VC++中,一个对象是这样保存虚函数表和成员变量的。(注:以下实验环境为Windows8.1, IDE是Visual Studio 2013。)

虚函数表

这里面是同一个类的两个不同对象所保存的内容。我们可以看到对象先保存了一个叫做_vfptr的变量。让我yy一下,它应该是Virtual function ptr的缩写,好像是这意思哦。他其实就是虚函数表,然后后面就是成员变量。

delete_this6

这一张图片里面是两个类的两个指针对象。其中p3的类是CC,继承于p2的类DelThis。p3申明的是基类DelThis的指针,但是我么看到,p3和p2的虚函数表的指向的内容是不一样的,具体函数也是不同类的成员函数。

     所以,在删除一个对象的时候,我们删除的只是它的虚函数表和成员变量,其他东西什么都没有动。

二、delete this

    接下来来说说这个奇葩的delete this。首先要明确一点,delete this这个在语法上是绝对没有错误的。但是使用的时候一定的注意了。首先,在前面就说过了,不能在析构函数中使用delete this,这样会造成栈溢出。有下图为证:

delete_this4

Stack overflow就是栈溢出。接着看看下图:

delete_this5

  程序因为栈溢出中断以后,可以看到程序在中断前不断的在调用DelThis类的析构函数。然后一直没有一个终止的时候,所以调用多了以后也就成了栈溢出。

但是我们在非析构函数的成员函数中使用delete this则不会出现问题。在调用delete this前,this指针指向的内容如下图:

delete_this1

在使用delete this以后,this指向的内容就变成了下图这样:

delete_this2

所以在一般的成员函数中使用delete this是不会出现问题得(包括在const函数中使用delete this也是没有问题得)。

但是在使用delete this的时候一定要小心了,这个对象一定得是保存在堆中的,不然会发生下面的错误:

delete_this3

  delete this在COM中的应用主要是删除COM对象的。COM对象一般是用户通过COM组件提供的类厂建立的,但是又没有提供释放的函数,而且让用户来处理COM对象的释放本来就是一种非常麻烦而又不太可能实现的事(这种感觉应该就像是,智能指针的释放要自己手动释放)。只有通过在减少引用计数的函数中判断,如果引用计数为0了那么久释放这个COM对象的资源,但是引用计数的函数都是COM对象提供的,所以只能选择delete this了。

好了delete this就写到这吧,欢迎大家拍砖。

《COM原理与应用》题外话——C++虚函数表和delete this