在控制台下调试一下如下代码:
#include <stdio.h>
class JCDad
{
public:
JCDad(int age):m_age(age){}
virtual ~JCDad(){}
virtual void print()
{
static int count;
count++;
printf("Hey, this is dad and I'm %d.\n",m_age);
printf("\tthe count is: %d.\n",count);
}
private:
int m_age;
};
class JCSon:public JCDad
{
public:
JCSon(int age):JCDad(55),m_age(age){}
virtual ~JCSon(){}
virtual void print()
{
printf("Hey, this is son and I'm %d.\n",m_age);
printf("my dad said: ");
JCDad::print();
}
private:
int m_age;
};
int main()
{
JCDad dad(50),*pdad;
JCSon son(21),*pson;
dad.print();
son.print();
son.JCDad::print();
pdad=&son;
// pson=&dad; //error:cannot convert from JCDad* to JCSon*, requires reinterpret_cast
pdad->print();
// pdad->JCSon.print(); //error:JCSon is't a member of JCDad
// pson->print();
return 0;
}
===============================================================================
运行结构如下:
如下给出两个对象的变量窗口:
如下给出给个成员的存储空间:
&dad=0x0012ff6c-------------------------------|
&dad.m_age=0x0012ff70 |---------sizeof(dad)=8
&dad+1=0x0012ff74---------------------------|
&son=0x0012ff5c
&son.JCDad::m_age=0x0012ff60--------------|
&son.m_age=0x0012ff64 |---------sizeof(son)=12
&son+1=0x0012ff68---------------------------|
如下给出了指针赋值后的变量窗口:
总结:
1. 从count变量的输出可以看出两个对象共享函数JCDad::print(),而从成员变量的地址空间可以看出两个对象拥有独立的地址空间而且空间大小不一定就是成员变量的空间大小之和,可能是考虑到数据对齐的缘故吧。其实这是必然的,从函数的调用就可以发现。
书中的原话是“同一个类的所有对象共享同一个成员函数的地址空间,而每个对象有独立的成员变量地址空间。也可以这样说成员函数式类拥有的,成员变量是类的对象拥有的”
2. 从对象的变量窗口中可以看出两个对象具有独立的虚函数表入口,在调用虚函数的时候就是通过这个表索引得到的
3. 经调试事实上virtual只需要在父类中申明就可以了,而且该virtual可以一直继承下去。但是考虑到代码的维护的问题因此可以在每个继承关系链上都添加virtual作为申明。
4. 注意到虚函数表指向的是指针值的最近继承链类对象的函数
下面给出了实例的内存图: