多态

来源:互联网 发布:网络加盟代理 编辑:程序博客网 时间:2024/05/29 07:54

C++之多态:

目录: 1.0 概念以及多态条件
            2.0 重写
            3.0 虚表
            4.0 哪些成员函数可以定义为虚函数?
            5.0 纯虚函数           
            6.0 继承中的多态(单继承,多继承,菱形继承,菱形虚拟继承)  
            7.0 虚函数的调用过程

1.0 概念以及多态条件       
       多态的概念: 同一个事物在不不同场景下的不同状态
       多态的分类  静态多态【有:函数重载,泛型编程】这个过程发生在编译期间。
                   动态多态:发生在程序运行时。
         着重于动态多态
      动态多态的实现条件:1.0 基类有virtual函数(在派生类中要对基类的virtual函数进行重写)。
                                          2.0通过基类的指针或引用来调virtual函数。

2.0 重写既然提出了重写,那么重写的条件: [不同的作用域中]【基类】【派生类】,在继承体系中,基类有虚函数并且派生类中也有同名的虚函数,积累和派生类虚函数的函数原型一样(返回值,函数名,参数列表),满足以上条件就构成了重写。
例外:重写了但是不全满足上面的条件
//协变:返回值不同class Base{public:virtual Base* Test(){}};class Derived:public Base{public:virtual Derived* Test(){}};//析构函数: 函数名不一样class Base{public:virtual ~Base(){}};class Derived :public Base{public:virtual ~Derived(){}};

3.0 虚表(有虚函数的类对象的前四个字节所存放的地址指向一个虚函数表即虚函数的地址)
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class Base{public:virtual void FunTest1(){cout << "Base::FunTest1" << endl;}virtual void FunTest2(){cout << "Base::FunTest2" << endl;}};class Derived :public Base{public:virtual void FunTest1(){cout << "Derived::FunTest1" << endl;}virtual void FunTest2(){cout << "Derived::FunTest2" << endl;}};void Test(){Base b;Base* b1 = &b;b1->FunTest2();b1->FunTest1();Derived d;b1 = &d;b1->FunTest1();b1->FunTest2();}int main(){Test();system("pause");return 0;}
结果:                                                                           &b的结果:
       基类的前4个字节是个地址,指向虚函数表(按照base中声明顺序排布ji顺序的问题可以验证,在基类中加一个虚函数,那么在虚表最后会多出一行(虚函数地址)   虚表结尾的标志是0000(环境:vs2013),其他的平台结尾标志可能不一样。

这也说明了多态性一个接口,多种方法 。
同一个类维护的是同一个虚表,与对象无关。
  4.0 哪些成员函数可以定义为虚函数?
  友元函数:不行,因为不满足动态多态的条件,类的成员函数。
  析构函数:可以,看个代码就明白了
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class Base{public:virtual ~Base(){cout <<"Base::~Base"<< endl;}};class Derived :public Base{public:virtual ~Derived(){cout << "Derived::~Base" << endl;}};void Test(){Base* b1 = new Derived();delete b1;b1 = NULL;}int main(){Test();system("pause");return 0;}
图一:这是在析构函数前加了vitual关键字后的结果
图二:未加virtual关键字
 《------图一:                                《------图二:

Base* b1=new Derived();
如果是简单的析构不会调用到derived的析构函数,除非加了virtual关键字
加了之后动态多态,b1指向的是 Derived在析构Derived最后调用了Base的析构函数。
构造函数:不可以,构造函数是为了创建类对象的,只有在构造函数执行完之后对象才创建成功,而虚函数的调用条件(基类的指针或者引用)没有对象哪里去调用虚函数?所以不行。
赋值运算符重载:没必要,因为已经有了"赋值兼容规则".
          赋值兼容规则--public继承
                1. 子类对象可以赋值给父类对象(切割/切片)
                2. 父类对象不能赋值给子类对象
static型类成员函数:
                                           C++中的static成员函数属于类而不是该类的对象。
                                           虚函数是多态面向对象的而静态函数是面向类的。
                                           虚函数也可以通过基类的指针或引用来调用,但是它没有thid指针,只能操作静态成员变量,局限性大。
                                           这样的函数重写没有意义。
 
5.0 纯虚函数
(基类中)在虚函数后面加上"=0";这样虚函数就变成了纯虚函数,纯虚函数是不能实例化的即不能创建对象的(不完整),但是可以创建类指针。 在派生类中重新定义后=才能实例化。以下代码可以说明:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;class Base{public:virtual void Test() = 0;};class Derived :public Base{public:virtual void Test(){cout << "Derived::Test()" << endl;}};class a{};void Test(){//Base b;//不能创建Base* b;Derived d;a bbb;cout <<sizeof(b)<<endl;cout << sizeof(d) << endl;cout << sizeof(b) << endl;}int main(){Test();system("pause");return 0;}
结果:
            存在一个问题空类的大小为什么是1?
 所谓类的实例化就是在内存中分配一块地址.(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.因为如果空类不隐含加一个字节的话,则空类就没有实例化了(因为类的实例化就是在内存中分配一块地址)。
6.0 继承中的多态(单继承,多继承,菱形继承,菱形虚拟继承)
   单继承:
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;typedef void(*PVTE)();class Base{public:virtual void Test1(){cout << "Base::Test1()" << endl;}virtual void Test2(){cout << "Base::Test2()" << endl;}int _b;};class Derived :public Base{public:virtual void Test1(){cout << "Derived::Test1()" << endl;}virtual void Test2(){cout << "Derived::Test2()" << endl;}int _d;};void PrintVTF(Base& b, char* str){cout <<str<<":"<<endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun+=1;}}void Test(){Base b;Derived d;b._b = 0;d._b = 1;d._d = 2;PrintVTF(b, "Base");PrintVTF(d, "Derived");}int main(){Test();system("pause");return 0;}
结果:


Base类模型:                             Base虚表:                      
         
 Derived类模型:                       Derived虚表:
       
多继承:

实例:
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>using namespace std;typedef void(*PVTE)();class B1{public:virtual void Test1(){cout << "B1::Test1()" << endl;}int _b1;};class B2{public:virtual void Test2(){cout << "B2::Test2()" << endl;}int _b2;};class Derived :public B1,public B2{public:virtual void Test1(){cout << "Derived::Test1()" << endl;}virtual void Test2(){cout << "Derived::Test2()" << endl;}virtual void Test3(){cout << "Derived::Test3()" << endl;}int _d;};void PrintVTF(B1& b, char* str){cout << str << ":" << endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun += 1;}}void PrintVTF(B2& b, char* str){cout << str << ":" << endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun += 1;}}void Test(){Derived d;d._b1 = 1;d._b2 = 2;d._d = 3;B1& b1=d;B2& b2=d;cout<<sizeof(b1)<< endl;cout << sizeof(b2) << endl;cout << sizeof(d) << endl;PrintVTF(b1,"Dreived.B1.VFT");  //打印派生类里的B1的虚表PrintVTF(b2,"Derived.B2.VTF");  //打印派生类里的B2的虚表}int main(){Test();system("pause");return 0;}
结果:
                

可以看出来d自己的虚函数在先继承的那个基类的虚表中

菱形继承:
实例:
#include<iostream>using namespace std;typedef void(*PVTE)();class Base{public:virtual void FunTest1(){}public:int _p1;};class C1 :public Base{public:virtual void FunTest1(){   cout <<"C1.FunTest1()"<< endl;}virtual void FunTest2(){ cout << "C1.FunTest2()" << endl;}public:int _p2;};class C2 :public Base{public:virtual void FunTest1(){cout << "C2.FunTest1()" << endl;}virtual void FunTest3(){cout << "C2.FunTest3()" << endl;}public:int _p3;};class D :public C1, public C2{public:virtual void FunTest2(){cout << "D.TunTest2()" << endl;}virtual void FunTest3(){cout <<"D.TunTest3()"<< endl;}public:int _p4;};void PrintVTF(C1& b, char* str){cout << str << ":" << endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun += 1;}}void PrintVTF(C2& b, char* str){cout << str << ":" << endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun += 1;}}int main(){D d;C1& c1 = d;C2& c2 = d;d.C1::_p1 = 1;d._p2 = 2;d._p3 = 3;d.C2::_p1 = 1;d._p4 = 4;cout <<sizeof(c1)<< endl;cout << sizeof(c2) << endl;cout << sizeof(d) << endl;PrintVTF(c1,"C1.VFT");PrintVTF(c2,"C2.VFT");system("pause");return 0;}
结果:
 

如果d有自己的虚函数那么他的地址在d先继承的那个基类的虚表里。
菱形虚拟继承:

实例:
#include<iostream>using namespace std;typedef void(*PVTE)();class Base{public:virtual void FunTest1(){}public:int _p1;};class C1 :virtual public Base{public:virtual void FunTest1(){cout << "C1.FunTest1()" << endl;}virtual void FunTest2(){cout << "C1.FunTest2()" << endl;}public:int _p2;};class C2 :virtual public Base{public:virtual void FunTest1(){cout << "C2.FunTest1()" << endl;}virtual void FunTest3(){cout << "C2.FunTest3()" << endl;}public:int _p3;};class D :public C1,public C2{public:virtual void FunTest1()  //D调用FunTest1时不明确是C1的还是c2的不知道。所以重写后保存在c1的虚表里。{                        //菱形虚拟继承是为了解决菱形继承的问题(空间冗余所以基类在D中应该只有一份。cout << "D.TunTest1()" << endl;}virtual void FunTest2(){cout << "D.TunTest2()" << endl;}virtual void FunTest3(){cout << "D.TunTest3()" << endl;}virtual void FunTest5(){cout << "D.TunTest5()" << endl;}public:int _p4;};void PrintVTF(C1& b, char* str){cout << str << ":" << endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun += 1;}}void PrintVTF(C2& b, char* str){cout << str << ":" << endl;PVTE* pFun = (PVTE*)*(int*)&b;while (*pFun){(*pFun)();pFun += 1;}}int main(){D d;C1& c1 = d;C2& c2 = d;d._p1 = 1;d._p2 = 2;d._p3 = 3;d._p4 = 4;cout << sizeof(c1) << endl;cout << sizeof(c2) << endl;cout << sizeof(d) << endl;PrintVTF(c1, "C1.VFT");PrintVTF(c2, "C2.VFT");system("pause");return 0;}


结果:



菱形虚拟继承模型:点击打开链接
 7.0 虚函数的调用过程

step 1:开始执行调用虚函数;(需要判断是基类还是派生类对象)

step 2:取得对象的虚表地址(通过虚表指针)(基类在对象的前4个字节)

step 3:虚表地址那里获得函数入口地址,即得到要调用的函数

step 4:根据虚表里的的地址找到函数,并调用函数。

普通函数调用: call 函数名

相比较之下虚函数调用多了虚表指针这一步,增加了内存开销以及时间冗余。

注意:虚表是面向类的,而不是对象,即(类的多个对象共享一张虚表)
原创粉丝点击