C++多态【上】

news2024/11/19 20:43:58

在这里插入图片描述

文章目录

  • 1.多态的介绍
    • 1.1概念
    • 1.2分类
  • 2.多态的实现
    • 2.1虚函数的介绍
      • 1.定义
      • 2.重写
        • 1.通常情况
        • 2.特例
    • 2.2多态构成条件
    • 2.3破坏多态的情况
      • 1.父类没有虚函数 子类有对应虚函数重写
      • 2.父类有虚函数 子类没有重写对应虚函数
    • 2.4虚析构和纯虚析构
      • 1.虚析构
      • 2.纯虚析构
      • 3.虚析构/纯虚析构存在的意义
    • 2.5总结及拓展
      • 1.final:修饰父类虚函数,表示该虚函数不能再被重写
      • 2.override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错
      • 3.重载、覆盖(重写)、隐藏(重定义)
    • 2.6抽象类
      • 1.概念
      • 2.接口继承和实现继承
      • 3.代码演示

1.多态的介绍

1.1概念

在这里插入图片描述
简单来说,同样一种行为,不同对象完成时会呈现不同的状态。比如:

  1. 吃东西 牛吃草 狼吃羊 猫吃鱼
  2. 买票 学生票 军人票 普通票

1.2分类

静态多态: 函数重载 和 运算符重载 — 编译阶段确定函数地址
动态多态: 虚函数 — 运行阶段确定函数地址

2.多态的实现

2.1虚函数的介绍

1.定义

被virtual修饰的类的成员函数
在这里插入图片描述

2.重写

1.通常情况

派生类重写基类的虚函数:派生类中有一个跟基类完全相同的虚函数(返回值类型、函数名字、参数列表完全相同)

2.特例

  1. 子类重写父类的虚函数【不加virtual也构成重写–继承基类的虚函数后实现】
  2. 协变:返回值是父子关系的指针或引用–返回值不同也构成重写

对照组:

class Animal
{
public:
   virtual void express()
   {
        cout << "我在疯狂动物叫" << endl;
   }
};

class Dog :public Animal
{
public:
    virtual void express()
    {
        cout << "我在疯狂狗叫" << endl;
    }

};
void func(Animal& animal)
{
    animal.express();
}
int main() 
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}

实验组1:

class Animal
{
public:
    virtual Animal* express()
    {
        cout << "我在疯狂动物叫" << endl;
        return this;
    }

};

class Dog :public Animal
{
public:
    virtual Dog* express()
    {
        cout << "我在疯狂狗叫" << endl;
        return this;
    }

};
void func(Animal& animal)
{
    animal.express();
}
int main()
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}

实验组2:

class Ox  //牛
{

};
class Bull :public Ox//公牛
{

};
class Animal
{
public:
    virtual Ox* express()
    {
        cout << "我在疯狂动物叫" << endl;
        return nullptr;
    }

};

class Dog :public Animal
{
public:
    virtual Bull* express()
    {
        cout << "我在疯狂狗叫" << endl;
        return nullptr;
    }

};
void func(Animal& animal)
{
    animal.express();
}
int main()
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}

2.2多态构成条件

  1. 指向子类对象的基类指针或引用调用虚函数
  2. 派生类重写基类的虚函数
class Animal
{
public:
   virtual void express()
   {
        cout << "我在疯狂动物叫" << endl;
   }
};

class Dog :public Animal
{
public:
    virtual void express()
    {
        cout << "我在疯狂狗叫" << endl;
    }

};
void func(Animal& animal)
{
    animal.express();
}
int main() 
{
    Animal animal;
    func(animal);
    Dog dog;
    func(dog);
    return 0;
}

在这里插入图片描述

例题

在这里插入图片描述

class A
{
public:
	virtual void func(int value = 1) 
	{ 
		cout << "A->" << value << endl; 
	}
	virtual void test() 
	{ 
		func();
	}
};
class B : public A
{
public:
	void func(int value = 0)
	{ 
		cout << "B->" << value << endl; 
	}
};
int main()
{
	A* pb = new B;  //B -> 1
	
	pb->test();
	return 0;
}

2.3破坏多态的情况

1.父类没有虚函数 子类有对应虚函数重写

class Dad
{
public:
	void Cook()
	{
		cout << "佛跳墙" << endl;
	}

	virtual void Work()
	{
		cout << "Work" << endl;
	}
	int _a = 0;
};

class Son : public Dad
{
public:
	virtual void Cook()
	{
		cout << "方便面" << endl;
	}

	int _b = 0;
};

void Test(Dad& p)
{
	p.Cook();
}

int main()
{
	Dad dad;
	Test(dad);

	Son son;
	Test(son);

	return 0;
}

父类没有虚函数表 编译时就已经确定了函数地址 即不论父类或子类对象调用的都是父类的成员函数

2.父类有虚函数 子类没有重写对应虚函数

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
using namespace std;

class Dad
{
public:
	virtual void Cook()
	{
		cout << "佛跳墙" << endl;
	}

	virtual void Work()
	{
		cout << "Work" << endl;
	}
	int _a = 0;
};

class Son : public Dad
{
public:
	/*virtual void Cook()
	{
		cout << "方便面" << endl;
	}*/

	int _b = 0;
};

void Test(Dad& p)
{
	p.Cook();
}

int main()
{
	Dad dad;
	Test(dad);

	Son son;
	Test(son);

	return 0;
}

父类有虚函数 运行时才确定函数地址 只不过子类没有重写 虚函数表存放的均为父类的虚函数地址 所以均调用父类的成员函数

2.4虚析构和纯虚析构

1.虚析构

1.一般情况:调用完子类析构后自动调用父类析构

class Person 
{
public:
	Person()
	{
		cout << "Person()" << endl;
	}

    ~Person() 
	{
		cout << "~Person()" << endl;
	}

	//int* _ptr;
};

class Student : public Person 
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}
	// 析构函数名底层为:destructor -- 构成虚函数重写
	~Student() 
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Student s;

	return 0;
}

在这里插入图片描述
2.没有虚析构导致错误的场景

class Person 
{
public:
	Person()
	{
		cout << "Person()" << endl;
	}

	~Person() 
	{
		cout << "~Person()" << endl;
	}

	//int* _ptr;
};

class Student : public Person 
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}
	// 析构函数名底层为:destructor -- 构成虚函数重写
	~Student() 
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Person* ptr1 = new Person;
	delete ptr1;

	Person* ptr2 = new Student;
	delete ptr2;
    
	return 0;
}

在这里插入图片描述
3.成功样例

class Person 
{
public:
	Person()
	{
		cout << "Person()" << endl;
	}

	virtual ~Person() 
	{
		cout << "~Person()" << endl;
	}

	//int* _ptr;
};

class Student : public Person 
{
public:
	Student()
	{
		cout << "Student()" << endl;
	}
	// 析构函数名底层为:destructor -- 构成虚函数重写
	virtual ~Student() 
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Person* ptr1 = new Person;
	delete ptr1;

	Person* ptr2 = new Student;
	delete ptr2;                  //ptr2 -> destructer();
                                  //operator delete(ptr2);
	return 0;
}

在这里插入图片描述

2.纯虚析构

class Dad 
{
public:

    Dad()
    {
        cout << "Dad 构造函数调用!" << endl;
    }

    //虚析构函数
    //virtual ~Dad()
    //{
    //    cout << "Dad 虚析构函数调用!" << endl;
    //}
    //**虚析构和纯虚析构都必须有函数【实现】
    //纯虚析构函数
    virtual ~Dad() = 0;

    virtual void Name() = 0;

};

Dad::~Dad()
{
    cout << "Dad 纯虚析构函数调用!" << endl;
}

class Son : public Dad
{
public:
    Son(string name)
    {
        cout << "Son 构造函数调用!" << endl;
        _name = new string(name);
    }
   
    ~Son()
    {
        cout << "Son 析构函数调用!" << endl;
        if (this->_name != NULL) 
        {
            delete _name;
            _name = NULL;
        }
    }

    virtual void Name()
    {
        cout << *_name << "是son的名字" << endl;
    }
public:
    string* _name;
};

int main() 
{

    Dad* dad = new Son("Mike");
    dad->Name();

    delete dad;

    return 0;
}

在这里插入图片描述

3.虚析构/纯虚析构存在的意义

  1. 虚析构语法:virtual ~类名(){}
    纯虚析构语法: virtual ~类名() = 0; 类名::~类名(){}
  2. 二者目的皆是能够【delete指向子类对象的父类指针】时正确调用析构函数。
    区别在于:纯虚析构适用于:当前基类作为一个抽象类,不想要实例化对象,只作为子类的父类,并且可以强制子类重写析构函数。
    但是使用虚析构和纯虚析构需要注意:二者必须有函数实现–虚析构在类内即可完成函数实现–纯虚析构需要在类外完成。
    如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

2.5总结及拓展

1.final:修饰父类虚函数,表示该虚函数不能再被重写

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

3.重载、覆盖(重写)、隐藏(重定义)

重载:

两个函数在同一作用域
函数名相同参数不同(类型个数顺序 )

重写(覆盖):

两个函数分别在基类和派生类的作用域
R_函数名/参数/返回值都必须相同(协变例外)
两个函数必须是虚函数

重定义(隐藏):子类和父类中有同名成员,直接访问时将屏蔽父类同名成员

两个函数分别在基类和派生类的作用域
函数名相同
两个基类和派生类的同名函数不构成重写就是重定义

2.6抽象类

通常多态中父类虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类称为抽象类
抽象类特点

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

1.概念

包含纯虚函数的类叫做抽象类(接口类),抽象类不能实例化出对象。
派生类只有重写纯虚函数后才能实例化对象。

  1. 纯虚函数规范了派生类必须重写
  2. 纯虚函数体现出了接口继承

2.接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实
现。
虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口【返回值函数名参数列表】,目的是为了重写,达成
多态,继承的是接口。如果不实现多态,不要把函数定义成虚函数。

3.代码演示

class Plant
{
public:
	virtual void Breath() = 0;
};
class Rose :public Plant
{
public:
	virtual void Breath()
	{
		cout << "Rose-疏影暗香" << endl;
	}
};
class Grass :public Plant
{
public:
	virtual void Breath()
	{
		cout << "Grass-生生不息" << endl;
	}
};
void Test()
{
	Plant* pRose = new Rose;
	pRose->Breath();

	Plant* pGrass = new Grass;
	pGrass->Breath();
}
int main()
{
	Test();
	return 0;
}

在这里插入图片描述

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

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

相关文章

vue项目配置MongoDB的增删改查操作

在Vue中配置MongoDB的增删改查操作&#xff0c;需要先安装mongoose模块来连接MongoDB数据库。 1. 在Vue项目的根目录中&#xff0c;使用命令行安装mongoose模块&#xff1a; npm install mongoose --save 2. 找到启动node的app.js文件&#xff08;我这里是在server文件中&…

Word转为PDF后图片模糊怎么办?Word转为PDF的技巧介绍

将Word文档转为PDF是我们日常办公和文档处理中常见的需求。PDF格式的优势在于跨平台兼容性、保留原始格式、文档保护以及方便共享和分发等方面。本文将探讨Word转为PDF后图片模糊怎么办?Word转为PDF的技巧有哪些?通过这些问题的答案&#xff0c;可以帮助您更好的利用文件转换…

Jenkins buildDescription 设置html格式及url

文章目录 1.首先安装插件2.更改配置3.接下来就可以在pipline里写脚本了 当然也可以插件方式配置示例1 脚本方式示例2 插件方式 搞了好多种方式都不成功后来发现是配置有问题&#xff0c;其实很简单&#xff0c;记录下也给想用此功能的朋友们一个示例&#xff0c;网上写的例子都…

蚂蚁集团SQLess 开源,与内部版有何区别?

当我们使用关系型数据库时&#xff0c;SQL 是联系起用户和数据库的一座桥梁。 SQL 是一种高度非过程化的语言&#xff0c;当我们在编写SQL 时&#xff0c;表达的是想要什么数据&#xff0c;而不是怎么获取数据。因此&#xff0c;我们往往更关心SQL 有没有满足业务逻辑&#xff…

【Windows 常用工具系列 11 -- 笔记本F5亮度调节关闭】

文章目录 笔记本 F 按键功能恢复 笔记本 F 按键功能恢复 使用笔记本在进行网页浏览时&#xff0c;本想使用F5刷新下网页&#xff0c;结果出现了亮度调节&#xff0c;如下图所示&#xff1a; 所以就在网上查询是否有解决这个问题的帖子&#xff0c;结果还真找到了&#xff1a;…

Sumo中Traci.trafficlight详解(上)

Sumo中Traci.trafficlight详解&#xff08;上&#xff09; 记录慢慢学习traci的每一天&#xff0c;希望也能帮到你 文章目录 Sumo中Traci.trafficlight详解&#xff08;上&#xff09;Traci.trafficlight信号灯参数讲解1.getAllProgramLogics(self,tlsID)2.getBlockingVehicle…

[uni-app]微信小程序隐私保护指引设置的处理记录

文章目录 微信幺蛾子资料搜集关键信息思路处理代码实现效果展示 微信幺蛾子 关于小程序隐私保护指引设置的公告 一切的起因就是上面这则公告. 2023年9月15日后 自2023年9月15日起&#xff0c;对于涉及处理用户个人信息的小程序开发者&#xff0c;微信要求&#xff0c;仅当开发…

电子班牌云平台系统——智慧校园管理工具,多媒体信息发布、走班排课、家校互通、物联控制、教务管理、考勤管理、素质评价、日常办公

电子班牌云平台源码&#xff0c;saas模式微服务架构 电子班牌是一款智慧校园管理工具&#xff0c;也是校园多媒体展示平台。智慧班牌融合了多媒体信息发布、走班排课、家校互通、物联控制、教务管理、考勤管理、素质评价、日常办公等一系列应用&#xff0c;是校园管理的现代化手…

React v6(仅支持函数组件,不支持类组件)与v5版本路由使用详情和区别(详细版)

1.路由安装(默认安装最新版本6.15.0) npm i react-router-dom 2.路由模式 有常用两种路由模式可选&#xff1a;HashRouter 和 BrowserRouter。 ①HashRouter&#xff1a;URL中采用的是hash(#)部分去创建路由。 ②BrowserRouter&#xff1a;URL采用真实的URL资源&#xff0c;…

接口自动化测试系列-excel管理测试用例

代码源码&#xff1a; 框架结构 核心代码 excel数据处理 from configureUtil.LogUtil import getlog logger getlog(targetNameHandleData) import xlrd from openpyxl import load_workbook,workbook from openpyxl.styles import Font, colors import openpyxl import o…

Docker-基础命令使用

文章目录 前言命令帮助命令执行示意图docker rundocker psdocker inspectdocker execdocker attachdocker stopdocker startdocker topdocker rmdocker prune参考说明 前言 本文主要介绍Docker基础命令的使用方法。 命令帮助 Docker命令获取帮助方法 # docker -h Flag shor…

网易低代码引擎Tango正式开源

一、Tango简介 Tango 是一个用于快速构建低代码平台的低代码设计器框架,借助 Tango 只需要数行代码就可以完成一个基本的低代码平台前端系统的搭建。Tango 低代码设计器直接读取前端项目的源代码,并以源代码为中心,执行和渲染前端视图,并为用户提供低代码可视化搭建能力,…

【vue2第十二章】ref和$refs获取dom元素 和 vue异步更新与$nextTick使用

ref和$refs获取dom元素 为什么会有 ref 和 $refs&#xff1f; 因为在vue页面中使用dom查找元素&#xff0c;不管你是不是在子组件里面查找&#xff0c;查找的都是整个页面的元素&#xff0c;如果你想查找单独组件里面的元素是不容易实现的&#xff0c;除非把每个组件的class写…

亚马逊云科技通过生成式AI,帮助清华RIOS加速计算和分析的处理效率

近日&#xff0c;硬件创建平台Efabless宣布了其第一届“生成式AI开源芯片设计挑战赛”&#xff08;AI Generated Open-Source Silicon Design Challenge&#xff09;的评选结果。来自清华大学的RISC-V国际开源实验室&#xff08;RIOS Lab&#xff09;团队基于亚马逊云科技云上科…

npm install依赖冲突解决办法

今天npm的时候发现报错&#xff0c;原来是依赖冲突了 npm后面加上这个指令就可以顺利的安装依赖了。问题主因就是不同开发用了不同版本node导致依赖版本不同&#xff0c;出现了成功冲突&#xff0c;这是段指令&#xff1b;它告诉npm忽略项目中引入的各个依赖模块之间依赖相同但…

桌面平台层安全随手记录

声明 本文是学习桌面云安全技术要求. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 桌面平台层安全 桌面接入安全 用户标识 一般要求 本项要求包括&#xff1a; a) 系统应为用户提供唯一的身份标识&#xff0c;同时将用户的身份标识与该用户的所…

FPGA/IC秋招面试题 1(解析版)

分享个人觉得遇到还不错的题&#xff0c;后续有会继续补充。。。 以下题目均来自网络平台&#xff0c;用于学习交流如有侵权立马删除!!! 1. Verilog语言中&#xff0c;下面哪些语句不可被综合() A. #delay语句 B. initial语句 C. always语句 D. 用gen…

软件测试师之数的表示

目录 一、数的进制(1)十进制&#xff1a;D(2)二进制&#xff1a;B(3)十六进制&#xff1a;H(4)八进制&#xff1a;O/Q 二、其他进制转十进制(1)二进制转十进制(2)十六进制转十进制(3)八进制转十进制 三、二进制与十六进制/八进制进行转换四、考法 一、数的进制 (1)十进制&…

怎么把pdf转换成高清图片?

怎么把pdf转换成高清图片&#xff1f;最近&#xff0c;我的同事遇到了一个问题&#xff0c;现在她需要将一些pdf文件转换成高清的图片&#xff0c;这件事情让让她感到非常无助&#xff0c;因为她非常着急需要将这些文件转换为图片格式&#xff0c;以便更好的在今后的工作中进行…

AJAX学习笔记5同步与异步理解

AJAX学习笔记4解决乱码问题_biubiubiu0706的博客-CSDN博客 示例 前端代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>演示AJAX同步和异步</title> </head> <body> <script…