多态_cpp

一、什么是多态?
1.多态的概念
在面向对象方法:向不同的对象发送同一个消息,不同的对象在接收的时会产生不同的行为(即方法)
在C++中,多态性的表现形式之一:具有不同功能的函数可用同一个函数名,这样就可以实现用一个函数名调不同内容的函数。
用现实生活中的例子来说一下动态,比如说:学校开学,不同身份的人会有不同的反应。比如老师要备课上课,学生要报名上课,保洁阿姨要打扫教室学校卫生……
2.分类
从系统实现的角度来看,多态可以分为两类:静态多态和动态多态(如下图所示)
这里写图片描述
【静态多态】
静态多态(早绑定):编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误
下面用代码来说明一下:

#include<iostream>
using namespace std;
int Add(int left, int right)
{
     return left+right;
}
double Add(double left, double right)
{
     return left+right;
}
float Add(float left, float right)
{
     return left+right;
}
int main()
{
     cout<<Add(1, 2)<<endl;
     cout<<Add(1, 'd')<<endl;
     cout<<Add(12.34f, 34.12f)<<endl;
     cout<<Add(1, "Hello")<<endl;//会报错
     system("pause");
     return 0;
}

这里写图片描述
【动态多态】
动态多态(晚绑定):在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应方法
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定

动态绑定的条件:
1.必须是虚函数(一定在派生类中对基类的虚函数进行重写)
2.通过基类类型的引用或者指针调用虚函数(调用虚函数的方法)
下面来看一个实例:

二、【虚函数】
虚函数就是在基类声明函数是虚拟的,并不是实际存在的函数,然后在派生类中才正式定义此函数
作用:允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或者引用来访问基类和派生类中的同名函数

  1. **重载&重写(覆盖)&重定义(隐藏)的区别
    重载:在同一作用域;函数名相同/参数不同;返回值可以不同
    重写(覆盖):不在同一作用域(分别在基类和派生类);函数名相同/参数相同/返回值相同(协变例外);基类函数必须有virtual关键字;访问修饰符可以不同
    重定义(隐藏):在不同作用域中(分别在基类和派生类);函数名相同;在基类和派生类中只要不构成重写就是重定义

2.协变:基类虚函数返回类型是基类的引用或指针,派生类的虚函数返回类型是派生类的引用或指针,但还要构成重写
代码:

class Base
{
public:
     virtual void Test1()
     {
          cout<<"Base::Test1()"<<endl;
     }
     virtual void Test2()
     {
          cout<<"Base::Test2()"<<endl;
     }
    void Test3()
     {
          cout<<"Base::Test3()"<<endl;
     }
     int _b;
};
class Derived : public Base
{
public:
     virtual void Test1()
     {
          cout<<"Derived::Test1()"<<endl;
     }    
     virtual void Test2()
     {
          cout<<"Derived::Test2()"<<endl;
     }
     virtual void Test3()
     {
          cout<<"Derived::Test()3"<<endl;
     }
     int _d;
};
void Test(Base& b)
{                // ecx<----b
     b.Test2();  // call Base::FunTest2
     b.Test1();
}

int main()
{
     cout<<sizeof(Base)<<endl;
     Base b;
     Derived d;
     Test(b);
     Test(d);
     return 0;
}

这里写图片描述
3.纯虚函数:在成员函数的形参列表后面写上=0,则成员函数就是纯虚函数。
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化处对象(一定要被继承)。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。

class Base
{
     virtual void Base() = 0;
public:
     int _b;
};
class Derived : public Base
{};

总结
1.派生类重写基类的虚函数实现多态,要求函数名/参数列表/返回值完全相同(协变除外)
2.基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性
3.如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加
4.虚表是所有类对象实例共用的

三、虚表指针
什么是虚表,虚表是如何构造的???

3.1 普通单继承
基类虚表:虚函数在类中的声明次序
派生类中的虚函数:1.拷贝基类的虚表 2.检测派生类中是否对基类中虚函数进行重写—–(如若有重写的)用派生类中重写的虚函数来替代相同偏移量位置的基类虚函数 3.在虚表之后添加派生类自己的虚函数
下面用代码来实现以下:

class Base
{
public:
     virtual void Test1()
     {
          cout<<"Base::Test1()"<<endl;
     }
     virtual void Test2()
     {
          cout<<"Base::Test2()"<<endl;
     }
    void Test3()
     {
          cout<<"Base::Test3()"<<endl;
     }
     int _b;
};
class Derived : public Base
{
public:
     virtual void Test1()
     {
          cout<<"Derived::Test1()"<<endl;
     }    
     virtual void Test2()
     {
          cout<<"Derived::Test2()"<<endl;
     }
     virtual void Test3()
     {
          cout<<"Derived::Test3()"<<endl;
     }
     virtual void Test4()
     {
          cout<<"Derived::Test4()"<<endl;
     }
     int _d;
};
typedef void (*FuncPtr)();
void Print(Base& b)
{
     FuncPtr* pFunc  = (FuncPtr*)(*(int*)&b);
     while(*pFunc)
     {
          (*pFunc)();
          pFunc++;
     }
}
void Test(Base& b)
{
     b.Test1();
     b.Test2();
     b.Test3();
}
int main()
{
     Base b;
     Derived d;
     Test(b);
     Test(d);
     return 0;
}

*注:通过基类的引用或指针调用虚函数时,调用基类还是派生类的虚函数,要根据运行时引用(指针)实际引用(指向)的类型确定,调用非虚函数时,则无论基类指向的是何种类型,都调用的是基类的函数

3.2多继承
基类虚表:虚函数在类中的声明次序
派生类中的虚函数:1.拷贝基类的虚表 2.检测派生类中是否对基类中虚函数进行重写—–(如若有重写的)用派生类中重写的虚函数来替代相同偏移量位置的基类虚函数 3.添加派生类自己的虚函数到第一张虚表后面(why??—–这样会使效率提高)
下面用代码来实现以下:

class B1
{
public:
     virtual void FunTest1()
     {
          cout<<"B1::FunTest1()"<<endl;
     }
     virtual void FunTest2()
     {
          cout<<"B1::FunTest2()"<<endl;
     }
     int _b1;
};
class B2
{
public:
     virtual void FunTest3()
     {
          cout<<"B2::FunTest3()"<<endl;
     }
     virtual void FunTest4()
     {
          cout<<"B2::FunTest4()"<<endl;
     }
     int _b2;
};
class D:public B2, public B1
{
public:
     virtual void FunTest1()
     {
          cout<<"D::FunTest1()"<<endl;
     }
     virtual void FunTest3()
     {
          cout<<"D::FunTest3()"<<endl;
     }
     virtual void FunTest6()
     {
          cout<<"D::FunTest6()"<<endl;
     }
     int _d;
};
typedef void (*FunPtr)();
void PrintVFT(B1& b)
{
     FunPtr* pfun = (FunPtr*)(*(int*)&b);
     while(*pfun)
     {
          (*pfun)();
          pfun++;
     }
     cout<<endl;
}
void PrintVFT(B2& b)
{
     FunPtr* pfun = (FunPtr*)(*(int*)&b);
     while(*pfun)
     {
          (*pfun)();
          pfun++;
     }
     cout<<endl;
}

int main()
{
     cout<<sizeof(D)<<endl;
     D d;
     B1& b1 = d;
     PrintVFT(b1);
     B2& b2 = d;
     PrintVFT(b2);
     return 0;
}

3.3菱形继承

class B
{
public:
     virtual void FunTest1()
     {
          cout<<"B::FunTest1()"<<endl;
     }
     virtual void FunTest2()
     {
          cout<<"B::FunTest2()"<<endl;
     }
     int _b;
};
class C1:public B
{
public:
     virtual void FunTest1()
     {
          cout<<"C1::FunTest1()"<<endl;
     }
     virtual void FunTest3()
     {
          cout<<"C1::FunTest3()"<<endl;
     }
     int _c1;
};
class C2:public B
{
public:
     virtual void FunTest1()
     {
          cout<<"C2::FunTest1()"<<endl;
     }
     virtual void FunTest4()
     {
          cout<<"C2::FunTest4()"<<endl;
     }
     int _c2;
};
class D:public C1, public C2
{
public:
     virtual void FunTest1()
     {
          cout<<"D::FunTest1()"<<endl;
     }
     virtual void FunTest3()
     {
          cout<<"D::FunTest3()"<<endl;
     }
     virtual void FunTest4()
     {
          cout<<"D::FunTest4()"<<endl;
     }
     virtual void FunTest5()
     {
          cout<<"D::FunTest5()"<<endl;
     }
     int _d;
};
typedef void (*FunPtr)();
void PrintVFT(C1& c)
{
     FunPtr* pfun = (FunPtr*)(*(int*)&c);
     while(*pfun)
     {
          (*pfun)();
          pfun++;
     }
     cout<<endl;
}
void PrintVFT(C2& c)
{
     FunPtr* pfun = (FunPtr*)(*(int*)&c);
     while(*pfun)
     {
          (*pfun)();
          pfun++;
     }
     cout<<endl;
}
int main()
{
     cout<<sizeof(D)<<endl;
     D d;
     d.C1::_b = 0;
     d._c1 = 1;
     d.C2::_b = 2;
     d._c2 = 3;
     d._d = 4;
     C1& c1 = d;
     PrintVFT(c1);
     C2& c2 = d;
     PrintVFT(c2);
     return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值