你所不知道的C++ 之 C++虚类模型

我们知道,一个C++类如果带有virtual关键字的函数,那么,它就是一个虚类。虚类都有虚函数表。这个虚函数表真的存在吗?能摸得着、看得见吗?

的确是可以的。下面我们就展示一下如何看到C++类的虚函数表。请注意,我使用的系统是Ubutu 10.04, g++ 4.6.3。

下面我们先定义一个简单的纯虚类和它的实现类:

class BaseClassNoDeconstructor {
public:
    virtual void func() = 0;
};

class VirtualClassNoDeconstructor : public BaseClassNoDeconstructor{
public:
    virtual void func() {
        cout << "VirtualClassNoDeconstructor func\n";
    }
};

然后,针对类VirtualClassNoDeconstructor,我们定义两个等价的struct:

struct VirtualClassNoDeconstructorMember;
struct VirtualClassNoDeconstructorVTable {
    void (*func)(VirtualClassNoDeconstructorMember*);
};
struct VirtualClassNoDeconstructorMember {
    VirtualClassNoDeconstructorVTable * vtable;
};

然后,我们用VirtualClassNoDeconstructorMember来调用它:

void dofuncNoDeconstructor(BaseClassNoDeconstructor* bcd) {
    cout <<"======= call by class no deconstructor by vtable: ===\n";
    VirtualClassNoDeconstructorMember * pvcd = (VirtualClassNoDeconstructorMember*)bcd;
    pvcd->vtable->func(pvcd);

    cout <<"======== end =======\n";
}

想看下输出结果吗?

======= call by class no deconstructor by vtable: ===
VirtualClassNoDeconstructor func
======== end =======

我想不用多说什么了。

那么,对于一个有虚析构造的类,情况又是什么呢?首先,还是先看简单的类:

class BaseClass {
public:
    virtual ~BaseClass() { }
    virtual void func() = 0;
};

class VirtualClass : public BaseClass {
public:
    virtual ~VirtualClass(){
        cout << "Virtual Class destory"<<endl;
    }
    virtual void func() {
        cout << "Virtual Class : v="<<value<<endl;
    }
    VirtualClass(int v) : value(v) { };
private:
    int value;
};

这两个类有虚的析构造,对应的等价struct则需要定义为:

struct VirtualClassVTable {
    void (*dector)(VirtualClassMember* self);
    void (*delete_obj)(VirtualClassMember* self);
    void (*func)(VirtualClassMember* self);
};

struct VirtualClassMember {
    VirtualClassVTable* vtable;
    int value;
};

看,多了两个函数:dector和delete_obj。先别着急,先看看测试代码和输出结果

void dofunc(BaseClass* bc) {
    cout <<"====== call class by vtable: ===\n";
    VirtualClassMember *pvcm = (VirtualClassMember*)bc;
    cout <<"from member value="<< pvcm->value<<endl;
    pvcm->vtable->func(pvcm);
    pvcm->vtable->dector(pvcm);
    printf("VTable %p: func=%p, ~=%p\n",pvcm->vtable, pvcm->vtable->func, pvcm->vtable->dector);

    printf("pvcm->vtable->delete_obj=%p\n", pvcm->vtable->delete_obj);
    printf("pvcm->vtable->dector=%p\n", pvcm->vtable->dector);
    printf("pvcm->vtable->func=%p\n", pvcm->vtable->func);
    cout <<"======== end ======\n";
}

输出结果呢,则是

====== call class by vtable: ===
from member value=1234
Virtual Class : v=1234
Virtual Class destory
VTable 0x401150: func=0x400970, ~=0x400ce2
pvcm->vtable->delete_obj=0x400d14
pvcm->vtable->dector=0x400ce2
pvcm->vtable->func=0x400970
======== end ======

按照通常的做法,增加了一个析构造,应该增加一个函数才对,而实际上却增加了两个。其中一个就是真正的构造,而另外一个,是伴随他产生的一个删除函数:delete_obj。

这个函数是由编译器自动产生的。那么,他的作用是什么?为了搞清楚这个问题,我用objdump反编译代码,从其中摘抄了下面一段:

0000000000400d14 <_ZN9BaseClassD0Ev>:
  400d14:	55                   	push   %rbp
  400d15:	48 89 e5             	mov    %rsp,%rbp
  400d18:	48 83 ec 10          	sub    $0x10,%rsp
  400d1c:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  400d20:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400d24:	48 89 c7             	mov    %rax,%rdi
  400d27:	e8 b6 ff ff ff       	callq  400ce2 <_ZN9BaseClassD1Ev>
  400d2c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400d30:	48 89 c7             	mov    %rax,%rdi
  400d33:	e8 d8 fb ff ff       	callq  400910 <[email protected]>
  400d38:	c9                   	leaveq
  400d39:	c3                   	retq  

这一段汇编代码的地址正是delete_obj函数指向的地址400d14。请看400d27位置的callq指令,调用的地址是400ce2。这个地址是dector的地址,即析构造的地址。再请看400d33的callq指令。[email protected]表示什么意思呢?@plt表示这是一个引入了其他动态库的函数。_ZdlPv用c++filt转换后,就是名字"operator delete(void*)"。

在这里,也提醒下各位,当使用虚类的时候,一定要带上虚的析构造函数。否则在删除一个虚类对象时,很可能会发生内存的泄漏!

时间: 2024-10-12 21:40:02

你所不知道的C++ 之 C++虚类模型的相关文章

你所不知道的html5与html中的那些事(三)

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些

Android中Context详解 ---- 你所不知道的Context

转载至 :http://blog.csdn.net/qinjuning 前言:本文是我读<Android内核剖析>第7章 后形成的读书笔记 ,在此向欲了解Android框架的书籍推荐此书. 大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中 时刻的在与它打交道,例如:Service.BroadcastReceiver.Activity等都会利用到Context的相关方法 : 说它陌生,完全是 因为我们真正的不懂Context

Android中Context详解 ---- 你所不知道的Context (转载)

Android中Context详解 ---- 你所不知道的Context (转载) http://blog.csdn.net/qinjuning 大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中 时刻的在与它打交道,例如:Service.BroadcastReceiver.Activity等都会利用到Context的相关方法 : 说它陌生,完全是 因为我们真正的不懂Context的原理.类结构关系.一个简单的问题是,一个应用

你所不知道的html5与html中的那些事第三篇

文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作都可以达到真正的云方式呢?这一系列的问题你是否已经想明白了呢? 本系列文章将为您一一解答你所不知道的关于html5与html中的那些事;具体会包括如:html5新的理念与想法,html5的新标签的用意与具体开发中场景应用,html5与css3的感情经历(用法搭配),包括html5的父亲html的一些

你所不知道的 URL

0.说明 第一幕 产品:大叔有用户反映账户不能绑定公众号.大叔:啊咧咧?怎么可能,我看看?大叔:恩?这也没问题啊,魏虾米.大叔:还是没问题啊,挖叉类.大叔:T T,话说产品姐姐是不是Java提供接口的时候,没有对URL进行encodeURI.产品:啊咧咧?我问问看? 第二幕 大叔:翔逼你给我过来!翔逼:啊咧咧?怎么了大叔?大叔:知道在URL中的+有时候会变成什么吗?翔逼:啊咧咧?不是+吗?大叔:知道在URL中的空格有时候会变成什么吗?翔逼:啊咧咧?不是空格吗?大叔:还不赶快自己去查查看!别也成了

Android中Context详解 ---- 你所不知道的Context(转)

Android中Context详解 ---- 你所不知道的Context(转)                                              本文出处 :http://blog.csdn.net/qinjuning 前言:本文是我读<Android内核剖析>第7章 后形成的读书笔记 ,在此向欲了解Android框架的书籍推荐此书. 大家好,  今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中 时刻的在与它打

Android Context完全解析,你所不知道的Context的各种细节

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47028975 前几篇文章,我也是费劲心思写了一个ListView系列的三部曲,虽然在内容上可以说是绝对的精华,但是很多朋友都表示看不懂.好吧,这个系列不仅是把大家给难倒了,也确实是把我给难倒了,之前为了写瀑布流ListView的Demo就写了大半个月的时间.那么本篇文章我们就讲点轻松的东西,不去分析那么复杂的源码了,而是来谈一谈大家都熟知的Context. Context相信所有

JavaScript中你所不知道的Object(二)--Function篇

上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.String.Function等有更多的内部属性,而它们分别是什么呢? 这些内部属性不能像Object的内部属性一样一言以蔽之,因为它们各有各的用处和特点.其中核心的部分自然是最特殊的对象,Function对象.我们先从简单的开始: [[PrimitiveValue]]: 值的类型是基础数据类型.所以所有

你所不知道的五件事情--java.util.concurrent(第二部分)

这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,仍然讲述了关于Java并发集合API的一些应用窍门,值得大家学习.(2010.06.17最后更新) 摘要:除了便于编写并发应用的集合API外,java.util.concurrent还引入了其它的预置程序组件,这些组件能辅助你在多线程应用中控制和执行线程.Ted Neward再介绍了五个来自于java.util.concurrent的Java编程必备窍门. 通过提供线程安全,性能良好的数据结构,并发