(黑马C++)L07 多态

news2024/11/24 17:22:52

一、多态的基本概念

多态是面向对象程序设计语言中除数据抽象和继承之外的第三个基本特征。

多态:父类的引用或者指针指向子类对象

C++支持编译时多态(静态多态)和运行时多态(动态多态),运算符重载和函数重载就是编译时多态,而派生类和虚函数实现运行时多态。

#include <iostream>
using namespace std;

class Animal{
public:
    void speak() {
        cout << "动物在说话" << endl;
    }
};

class Cat: public Animal{
public:
    void speak() {
        cout << "小猫在说话" << endl;
    }
};


void doSpeak(Animal & animal) { 
    animal.speak();
}

//如果发生继承关系,编译器允许进行类型转换
void test() {
    Cat cat;
    doSpeak(cat); //会输出动物在说话
}

int main() {
    test();
    system("pause");
    return 0;
}

早绑定,静态联编:上述结果会输出动物在说话,因为在调用doSpeak的时候,函数的地址已经绑定,直接找了animal类型的speak。

动态联编:如果想调用猫的speak,不能提前绑定函数地址,需要在运行时确定函数地址。动态联编的写法是将doSpeak写成虚函数 -- 在父类声明虚函数(子类中可写可不写),发生多态。

#include <iostream>
using namespace std;

class Animal{
public:
    virtual void speak() {
        cout << "动物在说话" << endl;
    }
};

class Cat: public Animal{
public:
    void speak() {
        cout << "小猫在说话" << endl;
    }
};


void doSpeak(Animal & animal) { //Animal & animal = cat
    animal.speak();
}

//如果发生继承关系,编译器允许进行类型转换
void test() {
    Cat cat;
    doSpeak(cat); //会输出小猫在说话
}

int main() {
    test();
    system("pause");
    return 0;
}

二、多态原理解析

Animal内部结构:

  • vfptr虚函数表指针,指针指向虚函数表,构造函数中会将虚函数表指针指向自己的虚函数表
  • 虚函数表中存放的是所有虚函数的地址
  • 子类写父类的虚函数speak,这种写法叫重写
  • 如果发生重写,会替换掉虚函数表原有的地址,比如Cat虚函数表会用&Cat::speak替换&Animal::speak
  • 重写必须返回值、参数个数、类型、顺序都相同

 以下函数会输出猫在说话,父类指针指向子类函数

void test02(){
    Animal* animal = new Cat;
    animal->speak();   
}

 三、多态案例-计算器案例

开发的原则:开闭原则 -- 对扩展开发,对修改关闭

利用多态实现计算器能够有利于代码后期的维护和扩展,无需修改原有代码,但缺点是效率较低,因为发生多态后内部的结构会更加复杂。

//利用多态实现计算器 -- 有利于维护和扩展,无需修改原有代码
#include<iostream>
using namespace std;

//抽象计算器类
class Calculator {
public:
	virtual int getResult() {
		return 0;
	}

	void setv1(int v) {
		val1 = v;
	}

	void setv2(int v) {
		val2 = v;
	}

	int val1;
	int val2;
};

//加法计算器类 -- 继承抽象计算器类
class PluseCalculator : public Calculator {
public:
	int getResult() { //重写父类的函数
		return val1 + val2;
	}
};

//减法计算器类 -- 继承抽象计算器类
class SubCalculator : public Calculator {
public:
	int getResult() { //重写父类的函数
		return val1 - val2;
	}
};

//进行测试
void test01() {
	Calculator* abc;
	//加法计算
	abc = new PluseCalculator;
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
	delete abc; //不赋为空
	//减法运算
	abc = new SubCalculator;
	abc->setv1(10);
	abc->setv2(20);
	cout << abc->getResult() << endl;
	return;
}

int main() {
	test01();
	system("pause");
	return 0;
}

四、抽象类和纯虚函数

虚函数 -- 上面案例中父类求结果的函数为虚函数   virtual int getResult() {return 0;}

由于该函数没有实质内容,所以可以写成纯虚函数。

纯虚函数 --  virtual int getResult() = 0;

  • 如果父类中有纯虚函数,则子类继承时必须要实现纯虚函数。
  • 如果父类中有纯虚函数,则这个父类就无法实例化对象。
  • 有纯虚函数的类通常称为抽象类,所以抽象类无法实例化对象。

五、虚析构和纯虚析构

以下代码中希望调用的是Cat的析构函数,但是实际运行中调用的是animal的析构函数,因为普通的析构是不会调用子类的析构,所以可能会造成释放不干净。

#include <iostream>
using namespace std;

class Animal{
public:
    virtual void speak() {
        cout << "动物在说话" << endl;
    }

    ~Animal() {
        cout << "Animal的析构调用" << endl;
    }
};

class Cat: public Animal{
public:
    Cat(const char* name) {
        this->m_Name = new char[strlen(name)+1];
        strcpy(this->m_Name, name);
    }
    
    void speak() {
        cout << "小猫在说话" << endl;
    }

    ~Cat() {
        cout << "Cat的析构调用" << endl;
        if(this->m_Name != NULL) {
            delete[] this->m_Name;
            this->m_Name = NULL;
        }
    }
    
    char* m_Name;
};


//如果发生继承关系,编译器允许进行类型转换
void test() {
    Animal* animal = new Cat("TOM");
    animal->speak();  //会输出小猫在说话
    delete animal; //调用的是animal的析构函数
}

int main() {
    test();
    system("pause");
    return 0;
}

利用虚析构解决通过父类指针指向子类对象释放时释放不干净导致的问题。

    virtual ~Animal() {
        cout << "Animal的虚析构调用" << endl;
    }

 纯虚析构:

纯虚析构需要声明,也需要实现(因为父类的析构也会调用到),并且在类内声明,类外实现。

如果类中出现了纯虚析构函数,这个类也算抽象类,不能实例化对象。

class Animal{
public:
    virtual void speak() {
        cout << "动物在说话" << endl;
    }

    virtual ~Animal() = 0;
};

Animal::~Animal() {
    cout << "Animal的纯虚析构调用" << endl;
}

六、向上类型转换和向下类型转换

向上类型转换:派生类转换为基类,安全,不会有数据的丢失。

Cat* cat = new Cat;
Animal* animal = (Animal*) cat;

向下类型转换:基类转换为派生类,不安全,会导致数据的丢失,原因是父类的指针或者引用的内存中可能不包含子类的成员的内存。

Animal* animal = new Animal;
Cat* cat = (Cat*) animal;

如果发生了多态,向上和向下类型转换都是安全的,强转之后可以操作全部的内存空间。

七、多态案例-PK游戏

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

纷享销客CRM让科顺营销人更容易呼唤到“炮火”

希望让听得见“炮声”的人&#xff0c;更容易呼唤到“炮火”。对于在一线做营销的人而言&#xff0c;他们就是听到“炮声”的人。让一线的人员听得到“炮声”也就是销售线索、商机&#xff0c;能呼唤到“炮火”也就是呼唤到他们需要的资源。这恐怕是所有营销人都希望达到的境界…

ubuntu Ad-Hoc组网通信

目录 WIFI通信的多种组网方式 1、AP模式 2、Ad-hoc模式 ubuntu18配置ad-hoc模式 WIFI通信的多种组网方式 1、AP模式 最常用的模式&#xff0c;需要一个节点&#xff08;一般是路由器&#xff09;作为AP&#xff0c;其他节点连接到这个AP产生的wifi网络。通信拓扑是星形&a…

11_6、Java集合之Map接口的使用

一、引入Map与Collection并列存在。用于保存具有映射关系的数据:key-value &#xff08;双列集合框架&#xff09;&#xff0c;Map 中的 key 和 value 都可以是任何引用类型的数据 。Map 中的 key 用Set来存放&#xff0c;不允许重复&#xff0c;即同一个 Map 对象所对应 的类&…

在rhel6系统部署iscsi远程存储

文章目录一 需求二 环境准备三 服务端配置3.1 添加硬盘3.2 安装软件3.3 编写配置文件3.4 启动服务3.5 检查配置信息四 客户端配置4.1 安装软件包4.2 启动服务4.3 发现目标4.4 登陆目标4.5 实现开机自动挂载五 对部署进行测试一 需求 1&#xff09;首先在服务端添加一块10G的硬…

实验二十二 配置访问控制列表AGL

实验二十二 配置访问控制列表AGL一、 ACL基础概念 1、访问控制列表根据源地址、目标地址、源端口或目标端口等协议信息对数据包进行过滤&#xff0c; 从而达到访问控制的目的 。可以在路由器、三层交换机等设备上使用 &#xff0c;目前部分新二层交换 机也支持ACL。 2、ACL由编…

十、k8s DashBoard

文章目录1 部署Dashboard2 使用DashBoard之前在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的。其实&#xff0c;为了提供更丰富的用户体验&#xff0c;kubernetes还开发了一个基于web的用户界面&#xff08;Dashboard&#xff09;。用户可以使用Dashboard部署容…

超级浏览器的技术原理,超级浏览器的浏览器指纹是什么?

浏览器指纹是超级浏览器的识别信息&#xff0c;网站可以通过这些信息来识别用户&#xff0c;判断用户的唯一性。常见的浏览器指纹有IP地址、浏览器所在地区、时区&#xff1b;用户代理&#xff08;User Agent&#xff09;相关的操作系统及版本、CPU 类型、浏览器及版本、浏览器…

吴恩达《机器学习》——PCA降维

PCA降维1. 主成分分析1.1 数据降维动机1.2 PCA降维目标问题分析2. PCA数学原理分析2.1 求协方差矩阵的碎碎念2.2 PCA实现方法3. Python实现3.1 进行人脸数据压缩数据集、源文件可以在Github项目中获得 链接: https://github.com/Raymond-Yang-2001/AndrewNg-Machine-Learing-Ho…

一些实用的办公工具分享给你

ABBYY FineReader 这是一个可以转换PDF格式的图片文字识别软件&#xff0c;下载之后可以免费试用七天&#xff0c;或者选择去它的网站上传PDF进行识别转换&#xff0c;一天最多可以转换10次&#xff0c;且一次只能转换3个页面。 【操作方法】 打开软件&#xff0c;点击“图像…

(day3)自学Java——面向对象

非原创&#xff0c;为方便自己后期复习 目录 1.类和对象 2.封装 3.就近原则和this关键字 4.构造方法 5.标准的javabean类 6.三种情况的对象内存图 7.基本数据类型和引用数据类型 8.this的内存原理 9.面向对象综合训练 (1)文字版格斗游戏 (2)两个对象数组练习 (3)对…

产品上新|语音识别+主题抽取,Magic Data多人会议数据集助您打造领先智能会议系统

2020年以来&#xff0c;新冠加快了线下向线上搬迁的速度&#xff0c;使得线上办公、在线教育、远程会议得到飞速普及和发展。艾媒咨询数据显示&#xff0c;2021年中国视频会议行业市场规模达148.2亿元。各类视频会议产品价格较低、操作便捷高效&#xff0c;普及率越来越高&…

vsftp开启登录,上传,下载,删除等操作审计日志

vsftp开启登录&#xff0c;上传&#xff0c;下载&#xff0c;删除等操作审计日志 背景 今天业务告知说有人把前天下午和昨天一天的ftp上面的附件被人删除了&#xff0c;首先我是非常的惊讶&#xff0c;居然会发生这种事&#xff0c;但是好在这个ftp不是我们负责的&#xff0c;…

驱动之设备模型

1. 起源与新方案 1.1 起源 仅devfs&#xff0c;导致开发不方便以及一些功能难以支持 热插拔不支持一些针对所有设备的同意操作&#xff08;如电源管理&#xff09;不能自动mknod用户查看不了设备信息设备信息硬编码&#xff0c;导致驱动代码通用性差&#xff0c;即没有分离设…

终章:学习路线

说明 该文章来源于徒弟lu2ker转载至此处&#xff0c;更多文章可参考&#xff1a;https://github.com/lu2ker/ 文章目录说明一些废话成果路线第一阶段要点第二阶段要点第三阶段要点第四阶段要点最后一些废话 截至这篇文章前已经有150star了&#xff0c;虽然比不上大佬们K级的量…

【链表】leetcode203.移除链表元素(C/C++/Java/Js)

leetcode203.移除链表元素1 题目2 思路 (两种方式&#xff09;2.1 在原来链表上进行删除2.2 设置一个虚拟头结点删除3 代码3.1 C &#xff08;两种方式&#xff09;3.2 C版本&#xff08;两种方式&#xff09;3.3 Java版本&#xff08;两种方式&#xff09;3.4 JavaScript版本4…

Vue的组件、组件的创建、data、methods

一、组件 组件是vue的重要的特征之一&#xff0c;可以扩展html的功能&#xff0c;也可以封装代码实现重复使用。 二、组件的创建 1. 非脚手架方式下创建 ​ 第一步&#xff1a;使用Vue.extend创建组件 ​ 第二步&#xff1a;使用Vue.component注册组件 ​ …

OpenGov(三):新波卡治理机制有哪些可期待?

OpenGov维持波卡开创的信念投票&#xff0c;与以前相同的方式进行&#xff0c;使用WebAssembly和几个链上投票机制。也就是说&#xff0c;OpenGov通过降低障碍&#xff0c;来更好地管理网络的日常决策&#xff0c;将流程推向去中心化。真正的重点是使提案的范围与通过治理流程的…

企业数字化转型到底是什么?

企业的数字化转型单单是从基础设施上变更&#xff0c;更要从企业数据从文化上入手&#xff0c;培养企业的数据文化&#xff0c;以数据驱动来促进业务发展。大家都把数据基础设施讲的很详细了&#xff0c;那么我就从企业的数据化转型当中的数据文化是什么&#xff1f;以下来为大…

Python类型注解(十)

python学习之旅(十) &#x1f44d;查看更多可以关注查看首页或点击下方专栏目录 一.为什么需要类型注解 在代码中提供数据类型的注解&#xff08;显式的说明&#xff09;&#xff0c;使用时能获得相关提示 帮助第三方IDE工具&#xff08;如PyCharm&#xff09;对代码进行类型推…

想从事网络信息安全的工作,该如何自学?

前言 【一一帮助网络安全入门和提升学习点这里一一】 由于我之前写了不少网络安全技术相关的文章&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人私信问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f;要学哪些东西&#…