C++多态

1.多态的概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会
产生出不同的状态

2.多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。在继承中,构成多态需要有两个条件:

(1)必须通过基类的指针或者引用调用虚函数。
(2)被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

2.1 虚函数

被virtual修饰的类成员函数称为虚函数。

2.1.1 虚函数构成重写的三个条件

虚函数的重写也称为覆盖。要求派生类中有一个虚函数与基类完全相同。完全相同指的是三同,返回值相同,参数列表相同,函数名相同。不符合重写的就是隐藏关系。

2.1.2 两个例外

(1). 协变(基类与派生类虚函数返回值类型不同)
派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指
针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。
class A{};
class B : public A {};
class Person {
public:
 virtual A* f() {return new A;}
};
class Student : public Person {
public:
 virtual B* f() {return new B;}
};

(2)析构函数的重写

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,
都与基类的析构函数构成重写。编译器统一处理成destructor。

2.2 重载、重写和重定义

3.抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口
类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生
类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

4.多态的原理

一个含有虚函数的类中会包含一个虚表指针,而虚表指针指向一个个虚函数的地址。看起来是一个虚函数指针数组。一般这个数组最后会放一个空指针。

派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,虚表指针也就是存在部分的另一部分是自己的成员。
虚表存的是虚函数地址,存在于代码段中,虚函数同样也存在于代码段中。

4.1 派生类的虚表生成

一般是先将基类中的虚表内容拷贝一份到派生类虚表中,如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数,派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

4.2 静态多态与动态多态

满足了多态的条件的函数调用是在运行中到对象中去寻找的,而不满足多态的会在编译时就确认好。

静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为也称为静态多态,比如:函数重载。
动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态

5.多继承中的虚函数表

多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中。

6.多态常考面试题

(1).以下程序输出结果是什么()

 class A
   {
   public:
       virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
        virtual void test(){ func();}
   };
   
   class B : public A
   {
   public:
       void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
   };
   
   int main(int argc ,char* argv[])
   {
       B*p = new B;
       p->test();
       return 0;
   }

A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确  

答案:B  虚函数重写是接口继承,重写实现,普通函数继承是实现继承。 p是new的B类,而p去调用test时,test里包含着A*的this指针,this指针调用func,func构成重写,满足多态条件,于是实现是类B的func,接口是类A。

(2)inline函数可以是虚函数吗?

答:可以,不过编译器就忽略inline属性,这个函数就不再是inline,因为虚函数要放到虚表中去。

(3)静态成员可以是虚函数吗?
答:不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。
(4) 构造函数可以是虚函数吗?
答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。
(5)析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
答:可以,并且最好把基类的析构函数定义成虚函数
(6)对象访问普通函数快还是虚函数更快?
答:首先如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。
(7)虚函数表是在什么阶段生成的,存在哪的?
答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值