纯虚函数
定义:在成员函数的形参列表后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数 在派生类中重新定义以后,派生类才能实例化出对象。
测试代码:
class Base//抽象类
{
public:
virtual void FunTest()=0;//纯虚函数
};
class Dervied:public Base
{
public:
virtual void FunTest()
{
cout<<"Dervied::FunTest()"<<endl;
}
};
int main()
{
//B b;//会报错
Dervied d;
d.FunTest();
return 0;
}
动态多态调用
测试代码:
class Base
{
public:
virtual void Fun()
{
cout<<"Base::Fun()"<<endl;
}
};
class Derived
{
public:
virtual void Fun()
{
cout<<"Derived::Fun()"<<endl;
}
};
void FunTest()
{
Base *pb=new Base;
pb->Fun();
pb=(Base*)new Derived;
pb->Fun();
}
int main()
{
FunTest();
}
分析如下:
虚表剖析
对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针
测试代码:
class CTest
{
public:
CTest()
{
iTest = 10;
}
virtual void Fun()
{}
private:
int iTest;
};
int main()
{
CTest t;
t.Fun();
cout<<sizeof(t)<<endl;
return 0;
}
运行结果为:
具体分析:
所以,Base类对象的对象模型如下:
看一下下面两个例子。
(1)派生类继承基类,没有重写基类的虚函数。
测试代码:
class Base
{
public:
Base()
{
m_iTest = 10;
}
virtual void FunTest0()
{
cout<<"Base::FunTest0()";
}
virtual void FunTest1()
{
cout<<"Base::FunTest1()";
}
virtual void FunTest2()
{
cout<<"Base::FunTest2()";
}
private:
int m_iTest;
};
class Derived:public Base
{
public:
virtual void FunTest4()
{
cout<<"Derived::FunTest4()";
}
virtual void FunTest5()
{
cout<<"Derived::FunTest5()";
}
virtual void FunTest6()
{
cout<<"Derived::FunTest6()";
}
};
typedef void (*FUN_TEST)();
void FunTest()
{
Base base;
cout<< "Base vfptr:"<<endl;
for (int iIdx = 0; iIdx < 3; ++iIdx)
{
FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&base + iIdx));
funTest();
cout<< ": "<<(int *)funTest<<endl;
}
cout<<endl;
Derived derived;
cout<< "Derived vfptr:"<<endl;
for (int iIdx = 0; iIdx < 6; ++iIdx)
{
FUN_TEST funTest = (FUN_TEST)(*((int*)*(int *)&derived + iIdx));
funTest();
cout<< ": "<<(int *)funTest<<endl;
}
}
int main()
{
FunTest();
return 0;
}
具体分析如下: Base类的对象:
Derived类的对象:
(2)派生类继承基类,并重写基类的虚函数。
测试代码:
class CBase
{
public:
virtual void FunTest0()
{
cout<<"CBase::FunTest0()";
}
virtual void FunTest1()
{
cout<<"CBase::FunTest1()";
}
virtual void FunTest2()
{
cout<<"CBase::FunTest2()";
}
virtual void FunTest3()
{
cout<<"CBase::FunTest3()";
}
};
class CDerived:public CBase
{
public:
virtual void FunTest0()
{
cout<<"CDerived::FunTest0()" ;
}
virtual void FunTest1()
{
cout<<"CDerived::FunTest1()" ;
}
virtual void FunTest4()
{
cout<<"CDerived::FunTest4()" ;
}
virtual void FunTest5()
{
cout<<"CDerived::FunTest5()" ;
}
};
typedef void (*_pFunTest)();
void FunTest()
{
CBase base;
for (int iIdx = 0; iIdx < 4; ++iIdx)
{
_pFunTest pFunTest = (_pFunTest)(*(( int*)*(int *)&base + iIdx));
pFunTest();
cout<< ": "<<(int *)pFunTest<<endl;
}
cout<<endl;
CDerived derived;
for (int iIdx = 0; iIdx < 6; ++iIdx)
{
_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&derived + iIdx));
pFunTest();
cout<< ": "<<(int *)pFunTest<<endl;
}
}
int main()
{
FunTest();
return 0;
}
结果分析: CBase类对象:
CDerived类对象:
对象模型如下:
总结:
1. 动态多态的条件:
(1)基类中有虚函数,派生类中一定要重写基类的虚函数
(2)必须使用基类的指针或引用调用派生类的虚函数。
2.基类虚表中的虚函数顺序是按照虚函数在类中的声明顺序。
3.派生类虚表的形成过程(虚表存的是虚函数地址,以下为了说明方便,没有说存的是虚函数地址):
(1)先拷贝基类的虚函数
(2)派生类若重写了哪个虚函数,就替换对应的基类虚函数
(3)最后跟上派生类自己的虚函数。
4.多态调用过程:(虚函数)
(1)取虚表指针(对象的前4字节)
(2)取虚函数于虚表的偏移量
(3)调用虚函数