浅谈多态

封装可以使代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码复用
而多态的目的是为了接口重用
多态一个接口,多种方法”,在运行时期才决定调用的函数

class Base
{
public:
       void foo()
       {
              printf("1\n");
       }
       void fun()
       {
              printf("2\n");
       }
};
class Derived :public Base
{
public:
       void foo()
       {
              printf("3\n");
       }
       void fun()
       {
              printf("4\n");
       }
};
int main()
{
       Base a;
       Derived b;
       Base *ptr = &a;
       ptr->foo();   //1
       ptr->fun();   //2
       ptr = &b;     
       ptr->foo();   //1
       ptr->fun();   //2
}
class Base
{
public:
       virtual void foo()
       {
              printf("1\n");
       }
       void fun()
       {
              printf("2\n");
       }
};
class Derived :public Base
{
public:
       void foo()
       {
              printf("3\n");
       }
       void fun()
       {
              printf("4\n");
       }
};
int main()
{
       Base a;
       Derived b;
       Base *ptr = &a;
       ptr->foo();   //1
       ptr->fun();   //2
       ptr = &b;     
       ptr->foo();   //3
       ptr->fun();   //2
       return 0;
}

第一个本身就是基类指针,指的又是基类对象,所以ptr->foo(),ptr->fun()就是1,2
第二个ptr->foo(),ptr->fun()是基类指针指向子类对象,体现了多态的用法,ptr->foo()
是基类指针指向了一个虚函数,虚函数列表里面存放的是foo()虚函数的地址,指向的对象
不同,函数地址也就不同,这里将找到相应的子类foo()的地址,所以答案为3,ptr->fun()
由于ptr是一个基类指针,指向的是固定偏移量的函数,因此指向的还是基类里面的fun()
函数了,所以输出结果还为2

int main()
{
       Base a;
       Derived b;
       Derived *ptr = (Derived *)&a;
       ptr->foo();    //1
       ptr->fun();    //4
       return 0;
}

如果将测试代码改为Derived ptr = (Derived )&a;ptr->foo();ptr->fun();输出结果为1、4
这是一个子类指针去指向一个强制转换为子类地址的基类对象,ptr此时是子类指针,虽然被赋予了基类对象地址,但是在ptr->fun(),由于地址偏移量固定,偏移量是子类对象的偏移量,所以ptr->fun()输出为4,ptr->foo(),通过虚函数表的引用,找到了基类foo()函数地址,所以输出为1
C++的多态是通过虚函数来实现的
虚函数:成员方法的返回值前面,加了vitual关键字
只要有虚函数,就会生成虚函数表,当一个类有虚函数表,它编译的时候会产生一张vftable虚函数表,虚函数表里面存放的是虚函数的地址(运行起来放在数据段)
覆盖(重写):子类重新定义父类的方法
重写有两种:(1)直接重写成员函数
(2)重写虚函数(体现了C++多态性)
重载:(1)允许多个同名的函数
(2)允许函数的参数列表不同,参数个数不同,或者两者都不同
(3)作用域相同
编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的 预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。
隐藏:(必须出现在继承结构中)派生类的函数屏蔽了与其同名的基类函数
(1)如果派生类的函数与其基函数同名,但是参数不同。此时,不论有没有vitual关键字,基 类的函数将被隐藏(不能与重载混淆)
(2)如果派生类的函数与其基类的函数同名,参数列表也相同,但是基类没有vitual关键字, 此时基类的函数将被隐藏(不能与覆盖混淆)

多态与非多态的区别:函数的地址是早绑定还是晚绑定,(1)在编译时期就能确定函数的调用地址, 并生产代码,是静态的,早绑定
(2)在运行时期才能确定的,是晚绑定
常见用法:声明基类指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据子类的不同实现不同的方法
纯虚函数:是在基类中声明的虚函数,在基类中没有定义,但要求任何派生类都要定义自己的方法
vitual void fun() = 0;
场景:基类生成对象是不合理的,例如:图形是一个抽象概念,它不可以实现求面积方 法,但它派生出的子类(圆,长方形)就可以定义求面积的方法
抽象类:包含纯虚函数的类称为抽象类,由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象 类的对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值