#include<iostream>
#include <string>
#include <stdio.h>
using namespace std;
class Animal
{
public:
Animal(int a = 1, int b = 2)
{
this->a = a;
this->b = b;
}
virtual void sleep(int minutes = 30)
{
cout<<"animal sleep "<<minutes<<" minutes"<<endl;
}
virtual void breathe()
{
cout<<"animal breathe"<<endl;
}
virtual void eat()
{
cout<<"animal eat"<<endl;
}
private:
int a;
int b;
};
class Fish:public Animal
{
public:
Fish(int c) {
this->c = c;
}
virtual void breathe()
{
cout<<"fish breathe"<<endl;
}
virtual void sleep(int minutes = 50)
{
cout<<"fish sleep "<<minutes<<" minutes"<<endl;
}
virtual void swim(void) //Fish子类特有函数
{
cout<<"fish swim"<<endl;
}
private:
int c;
};
int main()
{
typedef void (*Fun1)(void); //指向breathe()函数
Fun1 pFun1 = NULL;
typedef void (*Fun2)(int); //指向sleep()函数
Fun2 pFun2 = NULL;
cout<<"<<<<<<Animal 对象打印信息>>>>>>"<<endl;
Animal an(1,2);
cout<<"Animal类虚函数表地址:"<<(int*)*(int*)&an<<endl;
cout<<"对象an内存模型中成员变量a = "<<* ((int*)&an+1)<<endl;
cout<<"对象an内存模型中成员变量b = "<<* ((int*)&an+2)<<endl;
pFun1 = (Fun1)*(int*)*(int*)&an;
pFun1();
pFun2 = (Fun2)*((int*)*(int*)&an+1);
pFun2(50);
pFun1 = (Fun1)*((int*)*(int*)&an+2);
pFun1();
cout<<"<<<<<<Fish 对象打印信息>>>>>>"<<endl;
Fish fh(5);
cout<<"Fish类虚函数表地址:" <<(int*)*(int*)&fh<<endl;
cout<<"对象fh内存模型中成员变量a = "<<* ((int*)&fh+1)<<endl;
cout<<"对象fh内存模型中成员变量b = "<<* ((int*)&fh+2)<<endl;
cout<<"对象fh内存模型中成员变量c = "<<* ((int*)&fh+3)<<endl;
pFun1 = (Fun1)*(int*)*(int*)&fh;
pFun1();
pFun2 = (Fun2)*((int*)*(int*)&fh+1);
pFun2(50);
pFun1 = (Fun1)*((int*)*(int*)&fh+2);
pFun1();
pFun1 = (Fun1)*((int*)*(int*)&fh+3);
pFun1();
cout<<"<<<<<<Animal指针指向Fish 对象打印信息>>>>>>"<<endl;
Animal *anPtr = &fh;
cout<<"Animal类指针指向Fish类虚函数表地址:" << (int*)*(int*)anPtr<<endl;
cout<<"Animal类指针指向Fish类所属内存区域a = "<<* ((int*)anPtr+1)<<endl;
cout<<"Animal类指针指向Fish类所属内存区域b = "<<* ((int*)anPtr+2)<<endl;
pFun1 = (Fun1)*(int*)*(int*)anPtr;
pFun1();
pFun2 = (Fun2)*((int*)*(int*)anPtr+1);
pFun2(50);
pFun1 = (Fun1)*((int*)*(int*)anPtr+2);
pFun1();
pFun1 = (Fun1)*((int*)*(int*)anPtr+3);
pFun1();
return 0;
}<<<<<<Animal 对象打印信息>>>>>>
Animal类虚函数表地址:0x8049178
对象an内存模型中成员变量a = 1
对象an内存模型中成员变量b = 2
animal sleep 134514480 minutes
animal breathe
animal eat
<<<<<<Fish 对象打印信息>>>>>>
Fish类虚函数表地址:0x8049160
对象fh内存模型中成员变量a = 1
对象fh内存模型中成员变量b = 2
对象fh内存模型中成员变量c = 5
fish sleep 134514480 minutes
fish breathe
animal eat
fish swim
<<<<<<Animal指针指向Fish 对象打印信息>>>>>>
Animal类指针指向Fish类虚函数表地址:0x8049160
Animal类指针指向Fish类所属内存区域a = 1
Animal类指针指向Fish类所属内存区域b = 2
fish sleep 134514480 minutes
fish breathe
animal eat
fish swim
由此可以得到,Animal类an对象与Fish类fh对象的内存布局如下:
如果一个类声明了虚函数,编译器就会在其声明的对象中的开始部分增加一个指向虚函数表指针vfptr。此实例中 Animal* anPtr 声明了一个指向Animal对象的指针,当Animal没有声明虚函数时,anPtr所指向的内容并不包括vfptr,当用Fish对象给它赋值时,只将 Fish中的Animal对象复制给anPtr所指向的Animal对象。因此anPtr->breathe()会调用Animal中的breathe()函数。
如果Animal中的breathe()是虚函数,那么当Animal *anPtr = &fh(或者Animal *anPtr = new Fish(5);)执行时,就会把Fish对象的vfptr 和Fish 对象中包含的Animal对象一同复制给anPtr所指向的对象。此后,anPtr所指向的vfptr 是Fish对象的vfptr,因此再调用anPtr->breathe()时,就会调用Fish类中的breathe()函数。
总结:对于虚函数调用来说,每一个对象内部都有一个虚表指针,该虚表指针被初始化为本类的虚表。在程序中,不管你的对象类型如何转换,该对象内部的虚表指针都是固定的,所以,才能实现动态的对象函数调用,这就是C++多态性实现的原理。此外,我们还可以看到,对于访问限定符仅仅作用在编译器层面上的,编译器制定了私有的成员变量不能被继承规则,然而在父类和子类的内存模型中可以看到,子类是在父类已有的内存模型空间中增加子类自有的成员变量。
其特点为:
1. 每一个类都有虚表。
2. 虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。如果重写了相应的虚函数,那么虚表中的地址就会改变,指向自身的虚函数实现。
3. 派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。
C++中的多态实现通过虚函数表来完成。每个对象包含一个指向虚函数表的指针,即使对象类型转换,其内部虚表指针保持不变,从而实现动态绑定。虚表在继承中保持并可能覆盖基类虚函数,且派生类虚函数地址顺序与基类一致。
1012

被折叠的 条评论
为什么被折叠?



