本人对C++一直没什么了解,有几次开发用到C++也都没有研究很深,后来跟朋友谈论集成,发现一旦深入内存,类、集成、虚表什么的就变的很美,于是想写下这篇博客记录探究的过程,本文可能有些地方出现错误欢迎大家提出。
一个简单的例子
先看下面这个简单的例子,我们创建一个基类(Base Class),通过简单的继承(Inherit)产生两个派生类(Derived Class),然后通过一个基类的指针去调用这两个派生类,并且调用类中的函数。
class Base {
public:
int a, b;
virtual void Show() { cout << "Base class" << endl; }
void Show2() { cout << "Base class show 2" << endl; }
};
class Derived1: public Base {
public:
void Show() { cout << "Derived class 1" << endl; }
void Show2() { cout << "Derived 1 class show 2" << endl; }
};
class Derived2: public Base {
public:
int c;
void Show() { cout << "Derived class 2" << endl; }
};
void PrintResult(void)
{
Base bobj;
Base *p;
Derived1 dobj1;
Derived2 dobj2;
p = &bobj;
p->Show();
p = &dobj1;
p->Show();
p = &dobj2;
p->Show();
}
输出结果如下:
Base class
Base class show 2
Derived class 1
Base class show 2
Derived class 2
基类中定义了虚函数Show(),两个派生类都对重新定义了Show(),虽然是使用基类的指针对类中Show()进行调用,但是,实际执行的是各个类中定义的Show()。但是作为对比,虽然Derived1中对Show2()也进行了定义,但是两次调用所执行的都是Base中定义的Show2()。原因就在于两者性质不同,虚函数的入口是保存在类的虚表中,程序运行的时候才能确定,也就是晚绑定,而Show2(),是直接与类进行绑定的,在程序编译的时候就完成了绑定,也就是早绑定。
本文剩余的部分几乎全部用来讨论虚表的构建和内存结构,来搞清楚继承的时候到底发生了什么。
一个类的虚表
这次换一个稍微复杂一点的:
class Base {
public:
int i, j;
virtual void a() { cout << "Base::a" << endl; }
virtual void b() { cout << "Base::b" << endl; }
virtual void c() { cout << "Base::c" << endl; }
};
使用gdb进行调试,打印出类所占内存的数据:
(gdb) x /4xg 0x400f70
0x400f70 <_ZTV4Base+16>: 0x0000000000400d24 0x0000000000400d4e
0x400f80 <_ZTV4Base+32>: 0x0000000000400d78 0x6465766972654437
(gdb) x /4xg 0x400f30
0x400f30 <_ZTV7Derived+16>: 0x0000000000400da2 0x0000000000400d4e
0x400f40 <_ZTV7Derived+32>: 0x0000000000400d78 0x0000000000000000