一,概述
1)接口的多种不同的实现方式即为多态。
2)多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在C++中都是通过虚函数(Virtual Function) 实现的。
3)关键点:多态还有个关键之处就是一切用指向基类的指针或引用来操作对象
二,示例
1)普通的对象操作方式
#include <iostream> using namespace std; class A{ public: void print() { cout<<"This is A"<<endl; } }; class B:public A{ public: void print() { cout<<"This is B"<<endl; } }; int main(){ //为了在以后便于区分,我这段main()代码叫做main1 A a; B b; a.print(); b.print(); }
输出:This is A
This is B
2)按照多态的操作对象方式:通过父类对象来操作子类对象
#include <iostream> using namespace std; class A{ public: void print() { cout<<"This is A"<<endl; } }; class B:public A{ public: void print() { cout<<"This is B"<<endl; } }; int main(){ //通过父类来操作子类 A a; B b; A* p1=&a; A* p2=&b; p1->print(); p2->print(); }
输出:This is A
This is A
这跟我们预期的不一样
3)采用多态,就是在父类函数前面加上virtual
根据不同的类对象,调用其相应的函数,这个函数就是虚函数
#include <iostream> using namespace std; class A{ public: virtual void print() { cout<<"This is A"<<endl; } }; class B:public A{ public: void print() { cout<<"This is B"<<endl; } }; int main(){ //通过父类来操作子类 A a; B b; A* p1=&a; A* p2=&b; p1->print(); p2->print(); }
三,虚函数进阶
void (A::*fun)(); //定义一个函数指针
A *p=new B;
fun=&A::fun;//是真正获得虚函数的地址吗? 其实间接获得虚函数地址的一段代码的地址
#include <iostream> using namespace std; class A{ public: virtual void fun() { cout<<"A::fun"<<endl; } virtual void fun2() { cout<<"A::fun2"<<endl; } }; class B:public A{ public: void fun() { cout<<"B::fun"<<endl; } void fun2() { cout<<"B::fun2"<<endl; } }; void CallVirtualFun(void *pThis , int index=0) { void (*funptr)(void*); long lVptrAddr; memcpy(&lVptrAddr,pThis,4); memcpy(&funptr,reinterpret_cast<long*>(lVptrAddr)+index,4); funptr(pThis); } int main() { void (A::*fun)(); //定义一个函数指针 A *p=new B; fun=&A::fun;//是真正获得虚函数的地址吗? 其实间接获得虚函数地址的一段代码的地址 (p->*fun)();//B::fun fun = &A::fun2; (p->*fun)();//B::fun2 CallVirtualFun(p); //调用虚函数p->fun() CallVirtualFun(p,1);//调用虚函数p->fun2() delete p; system("pause"); return 0; }
四,补充重要知识
实例化类的虚函数必须有定义,原因如下:有虚函数作为成员函数的类, 它的实例化-对象, 在运行过程分配到的内存不止是它的成员数据, 还有一个指向该类虚函数表(vtable)的指针, 虚函数表中的每个数据项都是一个虚函数的入口地址;
如果一个对象的虚函数只有声明而没有实现, 就会出现这个虚函数表找不到本应作为其数据项之一的某函数的入口地址,
虚函数表在运行前不能装载完成, 所以产生连接错误!