当前位置: 首页 > news >正文

【c++】多态(一)

hello~ 很高兴见到大家! 这次带来的是C++中关于多态这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页: 默|笙

在这里插入图片描述


文章目录

  • 一、多态的概念
  • 二、多态的定义和实现
    • 1. 构成条件
    • 2. 虚函数
    • 3 .虚函数的重写
  • 三、协变与析构函数的重写
    • 1. 协变(了解)
    • 2. 析构函数的重写
    • 四、override和final关键字
  • 五、重载、重写和隐藏
  • 六、纯虚函数和抽象类


一、多态的概念

多态通俗来讲就是多种形态。多态分为编译时多态(静态多态)与运行时多态(动态多态)

  1. 编译时多态就是之前我们所学的函数重载和函数模板,通过传递不同的参数来达到实现多种多种形态。名称的来历是因为实参传给形参是在编译的时候完成的,而我们一般把编译时成为静态,运行时称为动态。
  2. 接下来所要学习的,是运行时多态。也就是在我们需要完成某个行为时(函数),会根据传递对象的不同而达到不同的结果,从而达到多种形态。比如学生和成年人同时买火车票。行为都是去买火车票,但是因为对象的不同,学生买的学是生票,成年人买的是全价票。

二、多态的定义和实现

多态是一个继承关系下的类对象(基类对象与派生类对象),用指向它们的基类指针去调用同一个函数,由于指向的对象不同,因而产生了不同的行为。

  1. 比如,有基类Person与派生类Student对象p和,调用同一个买票函数,用Person类指针ptr分别指向它们,结果是对象p买全价票,对象s买学生票,它们产生了不同的结果。
class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person
{
public:virtual void BuyTicket() { cout << "买票-半价" << endl; }
};void Func(Person* ptr)
{ptr->BuyTicket();
}int main()
{Person p;Student s;Func(&p);Func(&s);return 0;
}

在这里插入图片描述

1. 构成条件

要实现运行时多态,必须满足以下两个条件:

  1. 必须是基类的指针或引用调用虚函数。
  2. 被调用的函数必须是虚函数,并且完成了虚函数重写/覆盖。
  1. 对于必须是基类的指针或引用调用虚函数,这是因为运行时多态离不开继承(也就是基类和派生类),而只有基类的指针或者引用才能既指向基类对象又能指向派生类对象

2. 虚函数

类成员函数前面加virtual修饰,那么这个成员函数就被称为虚函数。非成员函数不能添加virtual进行修饰。

class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

3 .虚函数的重写

  1. 虚函数的重写: 派生类中有一个跟基类完全相同的虚函数,满足三同:函数名相同,参数列表与返回值完全相同,那么称派生类的虚函数重写了基类的虚函数
  2. 在重写虚函数时,派生类的虚函数可以不添加virtual关键字,这样也构成重写,但这种写法并不规范。
  3. 对于2,可以将派生类的虚函数看作是基类虚函数的定义(包含virtual) + 派生类虚函数的实现细节,这样派生类实际上也是有virtual关键字修饰的。比如:
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;
}
  1. 它的执行结果是 B->1。首先是一个B类型的指针p调用test()函数,由于B继承了A,所以p将会调用A类的test()函数(test()并没有构成重写),然后将p指针传递给test()函数里的A* const this,this是一个基类指针,且基类A与派生类B有同一个虚函数,满足重写。将会根据this指向的内容(指向B)而调用B里的虚函数,而因为B的虚函数是由A类虚函数声明+B类虚函数的实现(即 virtual void func(int val = 1) { std::cout << “B->” << val << std::endl; })。所以val的值为1。
  • 继承并不是把基类成员拷贝一份到派生类中去,通过一种层次结构使得派生类对象包含一个基类子对象。

三、协变与析构函数的重写

1. 协变(了解)

协变:派生类重写基类虚函数时,与基类函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或引用。

2. 析构函数的重写

  1. 基类的析构函数要设计为虚函数(加virtual),这样派生类析构只要定义,无论是否加virtual关键字,都构成重写。关于函数名:编译器会做特殊处理,析构函数的名称统一会被处理为destructor。这样符合三同,构成重写。
class A
{
public:~A(){cout << "~A()" << endl;}
};
class B : public A {
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};int main()
{A* p1 = new A;A* p2 = new B;delete p1;delete p2;return 0;
}

在这里插入图片描述

  1. 使用基类指针指向派生类对象并通过该指针删除对象时,若基类析构函数不添加virtual关键字,不构成重写,那么B的析构函数将不会被调用,资源不被释放,这会造成内存泄露问题。所以基类中的析构函数建议设计为虚函数。

四、override和final关键字

  1. 由于c++对于虚函数重写的要求比较严格,若有的时候因为函数名写错参数写错等无法构成重写,这种错误在编译期间是不会报出的,如果在程序运行时没有得到预期结果才去debug会得不偿失,因此c++11提供了override关键字,可以帮助用户检测是否完成了重写,如果没有完成,那么编译器将会报错。
格式:函数声明 override {}
如:virtual BuyTicket() override { cout << "全价买票" << endl; }
  1. 如果我们不想让派生类重写某个虚函数,那么可以用final关键字去修饰。
格式:函数声明 final {}
如:virtual BuyTicket() final {cout << "全价买票" << endl; }

final修饰类,类不能被继承,final修饰基类虚函数,虚函数不能被重写。

五、重载、重写和隐藏

  1. 由于这三种都有些相似之处,我们单独把它们拿出来做一下区分:

在这里插入图片描述

六、纯虚函数和抽象类

  1. 在虚函数的后面写上 =0,则这个函数为纯虚函数,纯虚函数不需要定义实现(因为它需要被派生类重写,但可以有显式的实现,但并不常见,因为意义不大),只要声明即可。包含纯虚函数的类就做抽象类,抽象类不能实例化出对象,如果派生类继承后不重写纯虚函数,那么派生类也是抽象类
  2. 纯虚函数某种程度上强制了派生类重写虚函数,因为不重写实例化不出对象。

今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

http://www.dtcms.com/a/393287.html

相关文章:

  • 01、Python从入门到癫狂:基础
  • uniapp 弹窗
  • 17.2 《16小时→2.3小时!多模态AI颠覆PPT制作:跨国企业实战验证》
  • MyBatis 从入门到实战:环境搭建与核心原理详解
  • 深入剖析陌讯AIGC检测算法:Transformer架构在AIGC识别中的技术创新
  • 【Ai智能客服上篇】
  • 《C++程序设计》笔记p3
  • 华为数字化转型战略框架:从“1套方法+4类场景+3个平台”的全景设计
  • Redis:主从复制与哨兵模式解析
  • 【中压选型篇】中压电源进线与变压器选型全指南:从拓扑设计到并联运行
  • 【精品资料鉴赏】数据治理咨询项目实施方案
  • 基于陌讯AIGC检测算法的局限性探讨:最大512Token输入下的长文本处理方案
  • 应用随机过程(三)
  • A/B测试:随机化与观察单位不一致,如何处理更科学
  • 树拍易购商业模式解析:创新与合规并行的数实融合样本
  • 使用递归求阶乘的和
  • HTML 结构与常用标签
  • AI 智能体开发工作流从哪些方面入手?
  • USBL与DVL数据融合的实时定位系统,MATLAB仿真
  • 端到端与世界模型(1):自动驾驶的基础模型从 VLM 到 VLA
  • Let’s Encrypt 免费SSL证书一键获取 - 网页版极简教程
  • IDEA指定配置文件启动
  • Python实现基于教学的优化器 (Teaching-Learning-Based Optimization, TLBO) (附完整代码)
  • 视频图像数据档案管理
  • 灰狼优化算法GWO
  • 2025csp入门组真题和解析
  • Keil-MDK程序运行和下载过程
  • 【Linux驱动】Linux字符设备框架
  • python、数据结构
  • 数字孪生能做什么?(续)