C++多态的认识与理解

本文围绕C++多态展开,介绍了多态的定义、类型,阐述构成多态的条件,如虚函数重写、父类指针或引用调用虚函数。还讲解了虚函数重写的例外、重写隐藏重载的区别、抽象类等知识,分析多态原理、多继承下的虚表,探讨构造、内联等函数能否为虚函数,并解析经典例题。

多态的定义

多态其实就是同一操作在不同的对象上可以有不同的实现方式。

多态的类型

多态分为静态多态和动态多态两种,而静态多态其实我们之前就了解过,今天主要是讲解一下动态多态。

  • 静态多态(编译时多态):静态多态其实就是在函数进行编译阶段根据函数的参数列表来确定调用哪个具体的函数的,其中主要就是函数重载和运算符重载。        
  • 动态多态(运行时多态):运行时多态是在程序运行阶段才能确定具体调用哪个函数(通过对象的虚表指针)。它是基于函数重写实现的。当通过基类指针或引用调用虚函数时,实际调用的是派生类中重写后的虚函数。

构成多态的条件

  1. 虚函数重写
  2. 必须通过父类的指针或引用去调用虚函数

虚函数重写

虚函数就是被virtual关键字修饰的成员函数

虚函数的重写就是派生类中有一个跟基类完全相同的虚函数,这就称子类的虚函数重写了基类的虚函数。而这里的完全相同是派生类虚函数与基类虚函数的返回值类型、函数名、参数列表类型完全相同 

class Person 
{
public:
	virtual void test() { ... }//虚函数
};
class Student : public Person 
{
public:
	virtual void test() { ... }//虚函数重写
};

注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,也是可以构成重写,因为继承时基类的虚函数被继承下来了,而在派生类依旧保持虚函数属性,所以可以不加virtual关键字修饰。但是有点不咋规范

 父类的指针或引用调用虚函数

首先我们要知道指针是一个变量,指向的是一个地址,而指针的类型恰恰就决定了通过该地址可以访问的空间大小。而引用是取别名(声明时初始化,之后不能绑定其他对象)。基类的指针或引用指向派生类的话,就会使得基类的对象指向的是派生类中基类的那一部分空间内容(类似于派生类将继承的基类部分切割)。所以当基类指针或引用的对象去调用虚函数时就形成了多态,从而调用的实际就是派生类的虚函数。而内部实现机制后文有解释。

多态调用实例

class Person 
{
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; return nullptr; }//
};
class Student : public Person 
{
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; return nullptr; }//
};
void Func(Person& p)//父类的引用为形参
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);//传递父类对象
	Func(st);//传递子类对象(形成多态)
	return 0;
}

通过指针或引用调用虚函数看的并不是通过参数的类型去调用对应的函数方法,而是该参数所实际指向的对象,指向父类对象就调用父类的虚函数,指向子类对象就调用子类的虚函数。

虚函数重写的两个例外

协变

协变就是虚函数重写的时候,基类和派生类的函数返回值类型可以不同但是必须是父子类关系的指针或引用。如下:

class A {};
class B :public A{};

class Person 
{
public:
	virtual A* test() {return nullptr; }//
};
class Student : public Person 
{
public:
	virtual B* test() {return nullptr; }//
};

就像上面的例子,父子类虚函数的返回值 必须是父子类关系的指针或引用,因为A类与B类也是父子关系,所以虚函数的返回值也应该将person类与student类的父子关系对应起来,即父类A对应父类Person,子类Student对应子类B。

析构函数的重写

其实基类与派生类的析构函数也是可以构成虚函数重写的,因为针对一些特定的情况,必须要使得基类与派生类实现重写才可以实现代码的正确安全性。

如以下情况:

看以下代码运行:

class Person {
public:
    Person()
    {
        cout << "Person()" << endl;
    }
    ~Person()
    {
        cout << "~Person()" << endl;
    }
};
class Student : public Person {
public:
    Student()
    {
        cout << "Student()" << endl;
    }
    ~Student()
    {
        cout << "~Student()" << endl;
    }
};
int main()
{
    Person* p1 = new Person;
    Person* p2 = new Student;
    delete p1;
    delete p2;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CR0712

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值