声明:以下测试均在windows7 32位vs2008环境下测试。
//什么是多态?
这里先引入虚函数和虚函数重写的概念。
虚函数:在类的成员函数前面加上virtual关键字 。(跟虚继承一点关系都没有,只不过关键字一样)。
虚函数重写:当在子类的定义了一个完全相同的虚函数时,则称子类的这个函数重写(也称覆盖 )了子类的这个虚函数。
所谓多态,其实就是“多种形态”。
c++中虚函数的主要作用就是实现多态。简单说父类的指针/引用调用重写的虚函数,当父类指针/引用指向父类对象时调用的是父类的虚函数,指向子类对象时调用的是子类的虚函数。(也就是说函数的调用跟对象有关)。
//多态的对象模型
(1)探索虚函数表
虚函数表是通过一块连续内存来存储函数的地址。这张表解决了继承、虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就想一张地图,指明了实际应该调用的虚函数函数。
(2)探索单继承对象模型
可以先看一段代码:
class Base
{
public:
virtual void func1()
{
cout<<"Base::func1"<<endl;
}
virtual void func2()
{
cout<<"Base::func2"<<endl;
}
public:
int _a;
};
class A :public Base
{
public:
virtual void func1()
{
cout<<"A::func1"<<endl;
}
virtual void func3()
{
cout<<"A::func3"<<endl;
}
virtual void func4()
{
cout<<"A::func4"<<endl;
}
public:
int _b;
};
void Test1()
{
Base b;
A a;
}
int main()
{
Test1();
return 0;
}
调试打开监视窗口:
可以看到派生类A::func1重写基类Base::func1,覆盖了相应虚表位置上的函数。
ps:可以看到这里没有看到你派生类A中的func3和func4,这两个函数就放在func2的后面,这里没有显示是VS的问题(BUG)。
但是我们可以通过一段代码将虚函数的地址和函数打印出来:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void func1()
{
cout<<"Base::func1"<<endl;
}
virtual void func2()
{
cout<<"Base::func2"<<endl;
}
public:
int _a;
};
class A :public Base
{
public:
virtual void func1()
{
cout<<"A::func1"<<endl;
}
virtual void func3()
{
cout<<"A::func3"<<endl;
}
virtual void func4()
{
cout<<"A::func4"<<endl;
}
public:
int _b;
};
typedef void(*FUNC)();
void PrintVTable(int * VTable)
{
cout<<"虚表地址>"<<VTable<<endl;
for(size_t i = 0;VTable[i] != 0;++i)
{
printf("第%d个虚函数的地址:0x%x,->",i,VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout<<"----------------------------------------"<<endl;
}
void Test1()
{
Base b;
A a;
PrintVTable((int*)(*(int*)&b));
PrintVTable((int*)(*(int*)&a));
}
int main()
{
Test1();
return 0;
}
(3)探索多重继承的内存格局
class Base1
{
public:
virtual void func1()
{
cout<<"Base1::func1"<<endl;
}
virtual void func2()
{
cout<<"Base1::func2"<<endl;
}
private:
int b1;
};
class Base2
{
public:
virtual void func1()
{
cout<<"Base2::func1"<<endl;
}
virtual void func2()
{
cout<<"Base2::func2"<<endl;
}
private:
int b2;
};
class A :public Base1,public Base2
{
public:
virtual void func1()
{
cout<<"A::func1"<<endl;
}
virtual void func3()
{
cout<<"A::func3"<<endl;
}
private:
int _a;
};
typedef void(*FUNC)();
void PrintVTable(int * VTable)
{
cout<<"虚表地址>"<<VTable<<endl;
for(size_t i = 0;VTable[i] != 0;++i)
{
printf("第%d个虚函数的地址:0x%x,->",i,VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout<<"----------------------------------------"<<endl;
}
void Test1()
{
A a;
}
int main()
{
Test1();
return 0;
}
调试打开监视窗口:
可以发现对象a中具有2个虚表。继承了基类中虚表。
通过代码将虚表打印出来:
void Test1()
{
A a;
PrintVTable((int*)(*(int*)&a));
//Base2虚函数表在对象Base1后面。
PrintVTable((int*)(*((int*)&a+sizeof(Base1)/4)));
}
int main()
{
Test1();
return 0;
}
可以看出派生类继承了父类的两个虚表,而派生类中没有重写基类中的虚函数时自己的虚函数放在第一个继承的基类的虚表后面。
//多态就是多种形态,c++的多态分为静态多态和动态多态。
1、静态多态就是重载,因为是在编译期决议确定,所以称为静态多态。
2、动态多态就是通过继承重写基类的虚函数实现的多态,因为实在运行期决议确定,所以称为动态多态。