【C++学习手札】多态:掌握面向对象编程的动态绑定与继承机制(初识)

 

                                               🎬慕斯主页修仙—别有洞天

                                              ♈️今日夜电波:世界上的另一个我

                                                                1:02━━━━━━️💟──────── 3:58
                                                                    🔄   ◀️   ⏸   ▶️    ☰  

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

首先了解形成多态的条件

接着了解多态的“三同”规则

思考

C++11 override 和 final

抽象类


首先了解形成多态的条件

        在C++中,形成多态主要依赖于以下几个条件:

  1. 继承:多态通常发生在类与类的继承关系中。一个基类(或称为父类)可以有一个或多个派生类(或称为子类),派生类继承并可能扩展基类的行为。
  2. 虚函数:在基类中声明虚函数是实现运行时多态的关键。通过在基类中使用virtual关键字声明成员函数,可以在派生类中重写这些函数,提供不同的实现。
  3. 动态绑定(迟绑定):当通过基类指针或引用调用虚函数时,具体调用哪个版本的函数(基类还是派生类的)是在程序运行时决定的,这称为动态绑定或迟绑定。这是多态行为的核心机制。
  4. 基类指针或引用:要实现多态,通常需要使用指向基类的指针或引用来操作派生类对象。这样,可以通过相同的接口来处理不同类型的派生类对象。
  5. 派生类重写虚函数:派生类必须重写基类中的虚函数以提供自己的实现。如果派生类没有重写虚函数,它将直接继承基类中的版本。
  6. 访问权限:确保派生类有足够的访问权限来重写基类中的虚函数。例如,如果基类的虚函数是私有的,则除了友元类之外,派生类将无法重写它。
  7. 析构函数的多态性:为了使通过基类指针删除派生类对象时能够正确调用派生类的析构函数,基类的析构函数应该被声明为虚函数。
  8. 纯虚函数和抽象基类:如果基类包含纯虚函数(使用= 0声明的虚函数),那么这个基类就是一个抽象基类,不能被实例化。派生类必须提供所有纯虚函数的实现,才能成为可实例化的具体类。
  9. 类型安全:C++中的RTTI(运行时类型信息)机制,如typeiddynamic_cast,允许在运行时检查和转换对象的类型,增加了多态的安全性。
  10. 编译器和内存管理:C++编译器在后台维护虚函数表(vtable)和管理动态内存分配,以确保多态行为的正确实现。

        简简单单的先看个例子吧:

#include<iostream>
using namespace std;

class Person
{
public:
	virtual void publish()
	{
		cout << "i am a Person" << endl;
	}
	virtual ~Person() { cout << "~Person()" << endl; }
};

class Student:public Person
{
public:
	virtual void publish()
	{
		cout << "i am a Student" << endl;
	}
	virtual ~Student() { cout << "~Student()" << endl; }
};

void fun(Person& a)
{
	a.publish();
}

void fun2(Person* A)
{
	A->publish();
}

int main()
{
	Person* cc = new Person;
	Student* ccc = new Student;
	fun2(cc);
	fun2(ccc);
	delete ccc;
	delete cc;
	cout << "_________________" << endl;
	Person aa;
	Student aaa;
	fun(aa);
	fun(aaa);

	return 0;
}

接着了解多态的“三同”规则

        在C++中,多态性(Polymorphism)主要指的是运行时多态,它允许不同类的对象通过相同的接口调用适当的方法。这种特性通常归纳为“三同”原则:

  1. 同函数名(Same Function Name):基类和派生类中的虚函数必须具有相同的函数名。这样,当通过基类的指针或引用调用该函数时,可以根据对象的实际类型来调用正确的函数实现。
  2. 同参数列表(Same Parameter List):基类和派生类中的虚函数应该具有相同的参数列表。这意味着函数的参数数量和类型应该相同,以确保在调用时传递的参数是一致的。
  3. 同返回类型(Same Return Type):基类和派生类中的虚函数应具有相同的返回类型,或者派生类中的返回类型应该是基类中返回类型的子类型(C++11起支持协变返回类型)。这是为了确保在使用基类指针或引用调用虚函数时,返回值的类型是一致的,从而可以进行正确的赋值或操作。

        需要注意的是,从C++11开始,引入了返回类型协变的概念,即派生类重写的虚函数可以返回基类中虚函数返回类型的子类型。例如:

class Base {
public:
    virtual Base* clone() const {
        // ...
    }
};

class Derived : public Base {
public:
    virtual Derived* clone() const override {
        // ...
    }
};

        在这个例子中,Derived::clone 函数的返回类型是 Derived*,它是 Base::clone 函数返回类型 Base* 的子类型,满足协变的要求。

思考

        请你思考一下以下代码的执行结果:

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

        分析如下:

        首先使用B类型的指针p指向了新申请的一块B类型的空间,然后调用由父类A继承下来的test()函数,此时需要注意的是:由于是父类继承下来的test()函数,那么这个函数的原型是不会改变的,那其中的this指针也是不会改变的(意思就是类型为A* 的this指针)。接下来就是多态的概念的了,是一个多态调用,派生类是可以不加virtual的,因为他会继承父类的函数声明,而子类会重写父类的实现,这里调用的是B的func()需要注意的是:父类与子类func()中的参数列表是遵循“三同原则”的。因此我们使用的是A的func的声明,也就是:virtual void func(int val = 1),但是使用的是子类的实现:{ std::cout << "B->" << val << std::endl; }。这里又有一点注意事项只有构成多态子类才会继承父类的接口,如果不构成那么就是正常调用!

C++11 override 和 final

        从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

  1. final:修饰虚函数,表示该虚函数不能再被重写。修饰类,表示该类不能被继承
//原型
class Car
{
public:
	virtual void Drive() {}
};
class Benz :public Car
{
public:
	virtual void Drive() { cout << "Benz-舒适" << endl; }
};
//修饰类
class Car final
{
public:
	virtual void Drive()  {}
};
class Benz :public Car
{
public:
	virtual void Drive() { cout << "Benz-舒适" << endl; }
};
//修饰虚函数
class Car 
{
public:
	virtual void Drive() final {}
};
class Benz :public Car
{
public:
	virtual void Drive() { cout << "Benz-舒适" << endl; }
};

        2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car{
public:
virtual void Drive(){}
};
class Benz :public Car {
public:
virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

抽象类

        抽象类是一种特殊的类,它不能被实例化,只能作为其他类的基类使用。

        在C++中,抽象类是一种特殊的类,它包含了至少一个纯虚函数(pure virtual function)。纯虚函数是一种没有实现的虚函数,它的声明以“= 0”结尾,表示该函数没有具体的实现,需要在派生类中被重写。

        抽象类的主要用途是为其他类提供一个公共的接口或基本的功能实现,同时强制要求派生类提供某些特定功能的实现。这样做的目的是为了保证派生类遵循某种规范或者具有某些特定的行为。由于抽象类不能被实例化,因此通常用来作为基类,通过继承机制来指导派生类的行为和属性。

        除了纯虚函数,抽象类也可以包含普通的成员函数和数据成员。这些普通成员可以在抽象类中实现,也可以留空让派生类去实现。如果一个类继承了抽象类但没有实现所有的纯虚函数,那么这个类也必须被声明为抽象类。

#include<iostream>
using namespace std;

class car
{
public:
	virtual void drive() = 0
	{}
};

class BMW : public car
{
public:
	virtual void drive()
	{
		cout << "i have a BMW" << endl;
	}
};

class Benz : public car
{
public:
	virtual void drive()
	{
		cout << "i have a Benz" << endl;
	}
};

void fun(car& aa)
{
	aa.drive();
}

int main()
{
	BMW a;
	Benz b;
	fun(a);
	fun(b);
	return 0;
}

 


                     感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                        给个三连再走嘛~  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/393538.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

删除链表的倒数第N个节点

删除链表的倒数第N个节点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#xff1f; 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例…

信息安全认证 | CISP证书怎么样?值得考吗?

HCIE考证研究所的朋友们&#xff0c;新年快乐&#xff01; 今天给大家说说CISP证书&#xff0c;新的一年祝大家逢考必过啊~ 01 考注册信息安全工程师证书的用处 CISP证书可作为学识和技能证明&#xff1b;求职、任职、晋升、加薪的资格凭证&#xff1b;用人单位招聘、录用劳动…

Python函数(一)

目录 一、定义函数 &#xff08;一&#xff09;向函数传递信息 &#xff08;二&#xff09;实参和形参 二、传递实参 &#xff08;一&#xff09;位置实参 &#xff08;二&#xff09;关键字实参 &#xff08;三&#xff09;默认值 &#xff08;四&#xff09;等效的函…

【监控】spring actuator源码速读

目录 1.前言 2.先搂一眼EndPoint 3.EndPoint如何被注入 4.EndPoint如何被暴露 4.1.如何通过http暴露 4.2.如何通过jmx暴露 5.EndPoint是怎么实现监控能力的 6.知道这些的意义是什么 1.前言 版本&#xff1a;spring-boot-starter-actuator 2.6.3 阅读源码一定要带着疑…

【C++学习手札】多态:掌握面向对象编程的动态绑定与继承机制(深入)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;世界上的另一个我 1:02━━━━━━️&#x1f49f;──────── 3:58 &#x1f504; ◀️ ⏸ ▶️ ☰ &am…

VQ30 广告点击的高峰期(order by和limit的连用)

代码 select hour(click_time) as click_hour ,count(hour(click_time)) as click_cnt from user_ad_click_time group by click_hour order by click_cnt desc limit 1知识点 order by和limit的连用&#xff0c;取出所需结果 YEAR() 返回统计的年份 MONTH() 返回统计的月份 D…

渗透测试练习题解析 4(CTF web)

1、[GXYCTF2019]禁止套娃 1 考点&#xff1a;git 泄露 进入靶场后只有一串文字&#xff0c;源代码、抓包之类的都没有敏感信息出现&#xff0c;直接用 kali 的 dirsearch 扫描 发现存在 .git 目录&#xff0c;猜测应该是源码泄露&#xff0c;使用 GitHack 扒一下源码&#xff0…

什么是生产排产管理系统?哪个最好用?

阅读本文&#xff0c;你将了解&#xff1a;一、生产排产管理系统是什么&#xff1b;二、生产排产管理系统的功能&#xff1b;三、盘点五款好用的生产排产管理系统&#xff1b;四、生产排产管理系统的优势。 一、生产排产管理系统是什么 生产排产&#xff0c;也叫生产计划排程…

详解Sora,为什么是AGI的又一个里程碑时刻?

文&#xff5c;郝 鑫 编&#xff5c;王一粟、刘雨琦 2024年伊始&#xff0c;OpenAI再向世界扔了一枚AI炸弹——视频生成模型Sora。 一如一年前的ChatGPT&#xff0c;Sora被认为是AGI&#xff08;通用人工智能&#xff09;的又一个里程碑时刻。 “Sora意味着AGI实现将从1…

基于Doris构建亿级数据实时数据分析系统

背景 随着公司业务快速发展&#xff0c;对业务数据进行增长分析的需求越来越迫切&#xff0c;与此同时我们的业务数据量也在快速激增、每天的数据新增量大概在30w 左右&#xff0c;一年就会产生1 个亿的数据&#xff0c;显然基于传统MySQL数据库已经无法支撑满足以上需求 基于上…

Bonjour Print Services

Bonjour Print Services &#xff08;apple mobile&#xff09; https://download.csdn.net/download/spencer_tseng/88845785

SQL110 插入记录(一)(插入和interval关键字的用法)

代码 insert into exam_record(uid,exam_id,start_time,submit_time,score) values(1001,9001,2021-09-01 22:11:12,2021-09-01 22:11:12interval 50 minute,90), (1002,9002,2021-09-04 07:01:02,null,null)知识点 interval关键字的用法 INTERVAL关键字一般使用格式为&#x…

0206-1-网络层

第 4 章 网络层 网络层提供的两种服务 虚电路服务 数据报服务 概要: 虚电路服务与数据报服务的对比 网际协议 IP 网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一。与 IP 协议配套使用的还有四个协议&#xff1a; 地址解析协议 ARP (Address Resolution Protocol)逆地…

【STM32】软件SPI读写W25Q64芯片

目录 W25Q64模块 W25Q64芯片简介 硬件电路 W25Q64框图 Flash操作注意事项 状态寄存器 ​编辑 指令集 INSTRUCTIONS​编辑 ​编辑 SPI读写W25Q64代码 硬件接线图 MySPI.c MySPI.h W25Q64 W25Q64.c W25Q64.h main.c 测试 SPI通信&#xff08;W25Q64芯片简介&am…

【数据结构与算法】递归、回溯、八皇后 一文打尽!

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

山西电力市场日前价格预测【2024-02-19】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-19&#xff09;山西电力市场全天平均日前电价为377.52元/MWh。其中&#xff0c;最高日前电价为557.55元/MWh&#xff0c;预计出现在08:15。最低日前电价为247.93元/MWh&#xff0c;预计…

个人简历补充

个人简历补充 1.对工作的认识2.八股文和知识面3.框架/架构角度深扒3.1 前端3.1.1 mPaaS&#xff08;移动领域&#xff09;3.1.2 普通前端项目框架3.1.3 微前端 3.2 后端 持续更新 1.对工作的认识 2.八股文和知识面 前端&#xff08;基础知识 / 开发能力 / 总结输出能力&#xf…

2.18总结

这两天在加强对最短路径的学习&#xff0c;除了Floyed比较简单之外&#xff0c;dijkstra、bell-mandford和SPFA学起来感觉有些难&#xff0c;洛谷上的模板题卡了半天都没写出来&#xff0c;最后只能通过看题解来帮助自己完成。 SPFA 算法实现&#xff1a; 最短路径算法对比比较…

嵌入式学习 C++ Day5、6

嵌入式学习 C Day5、6 一、思维导图 二、作业 1.以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴…

驶向未来:3D可视化模型重塑我们的道路认知

在科技的浪潮中&#xff0c;每一个革新都是对人类未来生活的深度洞察。而今&#xff0c;当可视化这一技术走进我们的视野&#xff0c;它不仅是一场视觉盛宴&#xff0c;更是一次对未来出行方式的全新探索。 一、从平面到立体&#xff0c;解锁道路新视角 你是否曾站在十字路口&…