class A
{
public :
A()
{
/*cout << "A()" << endl;*/
}
virtual void show()
{
cout << "A::show()" << endl;
}
virtual void show1()
{
cout << "A::show1()" << endl;
}
protected:
int _a;
};
A pa;
虚函数表就是通过一块连续内存来存储虚函数的地址.如上图所示,定义一个对象pa,监视窗口找到pa,pa的头上存放虚函数指针,指针指向虚表的地址.
为什么不将虚函数表存放在对象中,而是存放虚函数指针?
因为对象中可能有很多虚函数,存放在对象中会让对象变得很大,存放指针只需要占用一份空间.
同类型的对象共用同一份虚表:存放在常量区.
class A
{
public :
virtual void show()
{
cout << "A::show()" << endl;
}
virtual void show1()
{
cout << "A::show1()" << endl;
}
void show2()
{
cout << "A::show2()" << endl;
}
protected:
int _a;
};
class B:public A
{
public:
virtual void show()
{
cout << "B::show()" << endl;
}
virtual void show2()
{
cout << "B::show2()" << endl;
}
protected:
int _b;
};
如图所示,
在子对象的虚表是从父类虚表继承得来的,拷贝了父类的虚函数,如果在子类中该函数未定义,则不改变,如果定义了,则父类的虚函数被子类覆盖.再放入自己的虚函数指针
图中父类show被子类覆盖,子类未定义show1(),所以show1()没改变,子类定义了show2(),未出现在虚表中,通过代码打印来查看它是否存在.
typedef void(*FUNC) ();
void PrintVTable(int* VTable)
{
cout << " 虚表地址>" << VTable << endl;
for (int i = 0; VTable[i] != 0; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout << endl;
}
PrintVTable((int*)(*((int*)&pb)));//先取pb的地址&pb,再将该地址转为int*类型,
//指向头四个字节,解引用读到虚表指针的头四个字节的内容,此时为int类型,再将//*((int*)&pb)转化成指针类型.
结果如下:
由此可见.B类的虚函数指针被加入了虚表中.
class A
{
public :
virtual void show()
{
cout << "A::show()" << endl;
}
virtual void show1()
{
cout << "A::show1()" << endl;
}
void show2()
{
cout << "A::show2()" << endl;
}
protected:
int _a;
};
class B
{
public:
virtual void show()
{
cout << "B::show()" << endl;
}
virtual void show1()
{
cout << "B::show1()" << endl;
}
void show2()
{
cout << "B::show2()" << endl;
}
protected:
int _b;
};
class D :public A,public B
{
public:
virtual void show()
{
cout << "D::show()" << endl;
}
virtual void show1()
{
cout << "D::show1()" << endl;
}
virtual void show3()
{
cout << "D::show3()" << endl;
}
};
void test()
{ A pa;
B pb;
D d;
A* p1 = &d;
B* p2 = &d;
D* p3 = &d;
cout << "d="<
从图中可以看出,d的大小为16.这是因为B有两个虚基表,派生类将自己的虚函数指针放在了A的虚基表中.
对于父类指针p1来说,由于p1指向子类,且子类先继承A类,所以p1指向D的开头A,只能读取A部分
父类指针p2指向D中B,能读取中间的B.
子类指针p3指向D的首地址,能够读取整个D.
由于对象开头放的是A的
在多重继承中,派生类会把新增的虚函数指针放在首先继承的父类虚表中.并不是所有的虚函数都要去虚表中去找函数的地址,只有构成多态的时候调用虚函数才会去虚表中找.
A* pa=new A;
pa->show();
A a;
a.show();
图中show为虚函数,pa是父类指针构成多态,所以需要去虚表中查找,相比a的调用会多出很多操作..a是对象调用,不构成多态,直接调用函数.
菱形继承
class A
{
public :
A()
{
/*cout << "A()" << endl;*/
}
virtual void show()
{
cout << "A::show()" << endl;
}
virtual void show1()
{
cout << "A::show1()" << endl;
}
void show2()
{
cout << "A::show2()" << endl;
}
protected:
int _a;
};
class B:public A
{
public:
virtual void show()
{
cout << "B::show()" << endl;
}
virtual void show2()
{
cout << "B::show2()" << endl;
}
protected:
int _b;
};
typedef void(*FUNC) ();//定义一个函数指针
void PrintVTable(int* VTable)//打印虚函数指针的函数
{
cout << " 虚表地址>" << VTable << endl;
for (int i = 0; VTable[i] != 0; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout << endl;
}
class C :public A
{
public:
C()
{
/*cout << "C()" << endl;*/
}
virtual void show()
{
cout << "C::show()" << endl;
}
virtual void show1()
{
cout << "C::show1()" << endl;
}
protected:
int _c;
};
class D :public B,public C
{
public:
D()
{
/*cout << "D()" << endl;*/
}
virtual void show()
{
cout << "D::show()" << endl;
}
virtual void show1()
{
cout << "D::show1()" << endl;
}
int _d;
};
int main()
{
A pa;
B pb;
C pc;
D d;
cout << "d="<
如图所示:d中继承了两个虚表,分别是C的.
class A
{
public :
virtual void show()
{
cout << "A::show()" << endl;
}
int _a;
};
class B:virtual public A
{
public:
virtual void show()
{
cout << "B::show()" << endl;
}
virtual void show1()
{
cout << "B::show1()" << endl;
}
int _b;
};
typedef void(*FUNC) ();//定义一个函数指针
void PrintVTable(int* VTable)//打印虚函数指针的函数
{
cout << " 虚表地址>" << VTable << endl;
for (int i = 0; VTable[i] != 0; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout << endl;
}
class C :virtual public A
{
public:
virtual void show()
{
cout << "C::show()" << endl;
}
int _c;
};
class D :public B,public C
{
public:
//virtual void show()
//{
// cout << "D::show()" << endl;
//}
int _d;
};
对于上面的代码,如果将D中的内容屏蔽掉,编译不通过.
恢复之后,运行并观察d:
B,C各自拥有自己的虚表,A放在公共区.
此时,修改D,在D中参加自己的虚函数,
观察结果:
class D :public B, public C
{
public:
virtual void show()
{
cout << "D::show()" << endl;
}
virtual void showd()
{
cout << "D::showd()" << endl;
}
int _d;
};
此时B中多了一个指针.很显然是用来存放D自己的虚表.分别查看这两个地址,分辨哪一个是虚表,哪一个是虚基表.
很显然第一个地址存放的是几个地址.是虚表
第二个存放的是偏移量.是虚基表.
注意:对于C来说,如果自己没有虚函数,则就不需要虚表.
对于B来说,由于要存放D的虚表,除非D和B都没有自己的虚函数,他才没有虚表.
如果A,B,C,D都有自己的虚函数,那么:
class A
{
public :
virtual void show()
{
cout << "A::show()" << endl;
}
virtual void showa()
{
cout << "A::showa()" << endl;
}
int _a;
};
class B:virtual public A
{
public:
virtual void show()
{
cout << "B::show()" << endl;
}
virtual void showb()
{
cout << "B::showb()" << endl;
}
int _b;
};
class C :virtual public A
{
public:
virtual void show()
{
cout << "C::show()" << endl;
}
virtual void showc()
{
cout << "C::showc()" << endl;
}
int _c;
};
class D :public B, public C
{
public:
virtual void show()
{
cout << "D::show()" << endl;
}
virtual void showd()
{
cout << "D::showd()" << endl;
}
int _d;
};
观察d: