1、为什么了解决什么
多态指同一个实体同时具有多种形式。它是面向对象程序设计(OOP)的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的。C++中的多态性具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象使用的形式。(静态绑定和动态绑定)
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
C++中,实现多态有以下方法:虚函数,抽象类,覆盖,模板(重载和多态无关)。
多态就是允许方法重名 参数或返回值可以是父类型传入或返回。允许将子类类型的指针赋值给父类类型的指针。
其实如果是这样的目的话,函数指针也可以实现多态。或我们反过来虚函数就是通过类名查找其虚表指针*__vptr(void**)。
2、定义
简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。它的一般实现模型是:每一个类(class)有一个虚表(virtual table),内含该class之中有作用的虚(virtual)函数的地址,然后每个对象有一个vptr,指向虚表(virtual table)的所在。
这个类大小就会多4个字节,这4个字节就是VTABLE的地址,这个类里的每个虚函数都是这个VTABLE数组的成员。
3、实现原理工作机制
我们只要有虚函数的类,编译器都会为这个类创建一个VTABLE,用它来保存该类的所有虚函数地址。我们叫其函数指针数组。这个数组每个元素就是虚函数的地址。编译器给VTABLE置入了一指针VPTR。VPTR就是VTABLE的入口地址。VPTR的值取决于对像内存的地址。
每个类其VTABLE是独立唯一的,也就是说当构造该派生类实例(对象)时,那么这个类会有一个virtual table,而每一个实例(对象)都会有一个virtual pointer(以下简称vptr)指向该类的virtual function tabl
4、例题:#include <iostream>
//www.realtoptv.com by zhangyj
//========================================================
// 名字空间声明
//========================================================
using std::cout;
using std::endl;
//Y = 0.30R + 0.59G + 0.11B , U = 0.493(B-Y), V = 0.877(R-Y)
//R = Y + 1.4075 *(V - 128)
//G = Y – 0.3455 *(U –128) – 0.7169 *(V –128)
//B = Y + 1.779 *(U – 128)
//========================================================
//========================================================
class RGB2YUV
{
public:
//默认构造函数
RGB2YUV(int r = 1, int g = 1, int b = 1) :
m_r(r), m_g(g), m_b(b)
{
cout << "RGB2YUV constructor called" << endl;
}
//定义为虚函数
virtual int Y()
{
return 0.30*m_r + 0.59*m_g + 0.11*m_b;
}
virtual int U()
{
return 0.493*( m_b - Y() );
}
virtual int V()
{
return 0.877*(m_r - Y());
}
//显示
void ShowY()
{
cout << "Y volume is " << Y() << endl;
}
void ShowU()
{
cout << "U volume is " << U() << endl;
}
void ShowV()
{
cout << "V volume is " << V() << endl;
}
protected:
int m_r;
int m_g;
int m_b;
};
//========================================================
//========================================================
class PS_RGB2YUV : public RGB2YUV
{
public:
PS_RGB2YUV(int r, int g, int b) :RGB2YUV(r, g, b){}
//定义为虚函数
virtual int Y()
{
return 0.30*m_r + 0.59*m_g + 0.11*m_b+1;
}
virtual int U()
{
return 0.493*(m_b - Y())+1;
}
virtual int V()
{
return 0.877*(m_r - Y())+1;
}
};
//========================================================
//函数名 :main
//函数说明 虚函数的实现机制
//函数参数 :无
//返回值 :0
//========================================================
int main()
{
RGB2YUV Wyuv(32, 32, 32); //基类对象定义
PS_RGB2YUV Wyuv_offset(32, 32, 32); //派生类对象定义
//显示
Wyuv.ShowY();
Wyuv.ShowU();
Wyuv.ShowV();
//显示派生类对象
Wyuv_offset.ShowY();
Wyuv_offset.ShowU();
Wyuv_offset.ShowV();
//显示类的大小
cout << "Wyuv‘s size is " << sizeof(Wyuv) << endl;
cout << "Wyuv_offset‘s size is " << sizeof(Wyuv_offset) << endl;
system("pause");
}
8、反汇编分析
//定义为虚函数
29: virtual int Y()
30: {
013C4AA0 55 push ebp
013C4AA1 8B EC mov ebp,esp
013C4AA3 81 EC CC 00 00 00 sub esp,0CCh
013C4AA9 53 push ebx
013C4AAA 56 push esi
013C4AAB 57 push edi
013C4AAC 51 push ecx
013C4AAD 8D BD 34 FF FF FF lea ediebp-0CCh]
013C4AB3 B9 33 00 00 00 mov ecx,33h
013C4AB8 B8 CC CC CC CC mov eax,0CCCCCCCCh
013C4ABD F3 AB rep stos dword ptr es:[edi]
013C4ABF 59 pop ecx
013C4AC0 89 4D F8 mov dword ptr [this],ecx
//每个实例都有this,寄存器ECX里面的值作为this指针地址
31: return 0.30*m_r + 0.59*m_g + 0.11*m_b;
013C4AC3 8B 45 F8 mov eax,dword ptr [this]
013C4AC6 F2 0F 2A 40 04 cvtsi2sd xmm0,dword ptr [eax+4]
013C4ACB F2 0F 59 05 60 DC 3C 01 mulsd xmm0,mmword ptr ds:[13CDC60h]
013C4AD3 8B 4D F8 mov ecx,dword ptr [this]
013C4AD6 F2 0F 2A 49 08 cvtsi2sd xmm1,dword ptr [ecx+8]
013C4ADB F2 0F 59 0D C8 DC 3C 01 mulsd xmm1,mmword ptr ds:[13CDCC8h]
013C4AE3 F2 0F 58 C1 addsd xmm0,xmm1
013C4AE7 8B 55 F8 mov edx,dword ptr [this]
013C4AEA F2 0F 2A 4A 0C cvtsi2sd xmm1,dword ptr [edx+0Ch]
013C4AEF F2 0F 59 0D B0 DB 3C 01 mulsd xmm1,mmword ptr ds:[13CDBB0h]
013C4AF7 F2 0F 58 C1 addsd xmm0,xmm1
013C4AFB F2 0F 2C C0 cvttsd2si eax,xmm0
32: }
virtual int Y()//调了2次值
74: {
013C4A30 55 push ebp
013C4A31 8B EC mov ebp,esp
013C4A33 81 EC CC 00 00 00 sub esp,0CCh
013C4A39 53 push ebx
013C4A3A 56 push esi
013C4A3B 57 push edi
013C4A3C 51 push ecx
013C4A3D 8D BD 34 FF FF FF lea edi,[ebp-0CCh]
013C4A43 B9 33 00 00 00 mov ecx,33h
013C4A48 B8 CC CC CC CC mov eax,0CCCCCCCCh
013C4A4D F3 AB rep stos dword ptr es:[edi]
013C4A4F 59 pop ecx
013C4A50 89 4D F8 mov dword ptr [this],ecx
//每个实例都有this,寄存器ECX里面的值作为this指针地址
75: return 0.30*m_r + 0.59*m_g + 0.11*m_b+1;
013C4A53 8B 45 F8 mov eax,dword ptr [this] //调其实例
013C4A56 F2 0F 2A 40 04 cvtsi2sd xmm0,dword ptr [eax+4]
013C4A5B F2 0F 59 05 60 DC 3C 01 mulsd xmm0,mmword ptr ds:[13CDC60h]
013C4A63 8B 4D F8 mov ecx,dword ptr [this]
013C4A66 F2 0F 2A 49 08 cvtsi2sd xmm1,dword ptr [ecx+8]
013C4A6B F2 0F 59 0D C8 DC 3C 01 mulsd xmm1,mmword ptr ds:[13CDCC8h]
013C4A73 F2 0F 58 C1 addsd xmm0,xmm1
75: return 0.30*m_r + 0.59*m_g + 0.11*m_b+1;
013C4A77 8B 55 F8 mov edx,dword ptr [this] //调其实例
013C4A7A F2 0F 2A 4A 0C cvtsi2sd xmm1,dword ptr [edx+0Ch]
013C4A7F F2 0F 59 0D B0 DB 3C 01 mulsd xmm1,mmword ptr ds:[13CDBB0h]
013C4A87 F2 0F 58 C1 addsd xmm0,xmm1
013C4A8B F2 0F 58 05 98 DD 3C 01 addsd xmm0,mmword ptr ds:[13CDD98h]
013C4A93 F2 0F 2C C0 cvttsd2si eax,xmm0
76: }
9、总结
A、dynamic(late ) binding和static(early) binding 的实现优缺点
B、内存布局的优缺点
C、多重继承性的角度看优缺点
D、有多少种实现方法可以实现同样的功能