C++中的虚继承、多态以及模板的介绍

news2024/11/19 9:22:17

菱形继承

概念

菱形继承又称为钻石继承,由公共基类派生出多个中间子类,又由中间子类共同派生出汇聚子类。汇聚子类会得到中间子类从公共基类继承下来的多份成员

格式

                 A       --------公共基类
               /    \
              B      C    ------- 中间子类
               \    /
                  D       --------汇聚子类

存在的问题
汇聚子类会得到中间子类从公共基类继承下来的多份成员,造成空间浪费,这是完全没有必要的,而且还会对公共基类的成员多次初始化或释放

解决办法
采取虚继承

代码示例

#include <iostream>

using namespace std;

//封装公共基类 家具 类
class Jiaju
{
private:
    string color;
public:
    //无参构造
    Jiaju() {cout << "家具的无参构造函数" << endl;}

    //有参构造
    Jiaju(string n):color(n)
    {
        cout << "家具的有参构造函数" << endl;
    }
};

//中间子类
//封装 沙发的类
class Sofa:public Jiaju
{
private:
    string sitting;
public:
    //无参构造
    Sofa() {cout << "沙发的无参构造" << endl;}


    //有参构造函数
    Sofa(string s,string c):Jiaju(c),sitting(s)
    {
        cout << "沙发的有参构造" << endl;
    }
    void display()
    {
        cout << sitting << endl;
    }
};


//中间子类
//封装 床 类
class Bed:public Jiaju
{
private:
    string sleep;

public:
    //无参
    Bed() {cout << "床的无参构造" << endl;}

    //有参
    Bed(string s,string c):Jiaju(c),sleep(s)
    {
        cout << "床的有参构造" << endl;
    }
    void display()
    {
        cout << sleep << endl;
    }
};

//汇聚子类
//封装 沙发床类  继承于沙发 和 床
class Sofa_Bed:public Bed,public Sofa
{
private:
    int w;
public:

    //
    Sofa_Bed(){cout << "沙发床的无参构造" << endl;}

    //有参构造
    Sofa_Bed(string sit, string s, int w,string c):Bed(s,c),Sofa(sit,c),w(w)
    {
        cout << "沙发床的有参构造" << endl;
    }
};

int main()
{
//    Sofa_Bed s;
    Sofa_Bed s1("可坐","可躺",123,"pink");

    return 0;
}

虚继承

作用

可以让汇聚子类只保留一份,中间子类从公共基类继承下来的成员

格式

在中间子类的继承方式前 加上 关键字virtual

class 类名 : virtual 继承方式 类名   //中间子类
{
    中间子类的拓展;
};

注意

  • 中间子类虚继承公共基类后,汇聚子类的初始化列表,先调用中间子类的有参构造函数,中间子类再调用公共基类的有参构造函数;虚继承之后,只保留一份中间子类从公共基类继承下来的有参构造函数,意味着不知道调用哪一个中间子类继承下来的公共基类的构造函数,这样就会默认调用公共基类的无参构造函数
  • 如果汇聚子类想要对公共基类的数据成员初始化,需要显性调用公共基类的构造函数

代码示例

#include <iostream>

using namespace std;

//封装公共基类 家具 类
class Jiaju
{
private:
    string color;
public:
    //无参构造
    Jiaju() {cout << "家具的无参构造函数" << endl;}

    //有参构造
    Jiaju(string n):color(n)
    {
        cout << "家具的有参构造函数" << endl;
    }
};

//中间子类
//封装 沙发的类
class Sofa:virtual public Jiaju  //中间子类虚继承公共基类
{
private:
    string sitting;
public:
    //无参构造
    Sofa() {cout << "沙发的无参构造" << endl;}

    //有参构造函数
    Sofa(string s,string c):Jiaju(c),sitting(s)
    {
        cout << "沙发的有参构造" << endl;
    }
    void display()
    {
        cout << sitting << endl;
    }
};

//中间子类
//封装 床 类
class Bed:virtual public Jiaju  //中间子类虚继承公共基类
{
private:
    string sleep;

public:
    //无参
    Bed() {cout << "床的无参构造" << endl;}

    //有参
    Bed(string s,string c):Jiaju(c),sleep(s)
    {
        cout << "床的有参构造" << endl;
    }
    void display()
    {
        cout << sleep << endl;
    }
};

//汇聚子类
//封装 沙发床类  继承于沙发 和 床
class Sofa_Bed:public Bed,public Sofa
{
private:
    int w;
public:
    //
    Sofa_Bed(){cout << "沙发床的无参构造" << endl;}

    //有参构造
    Sofa_Bed(string sit, string s, int w,string c):Jiaju(c),Bed(s,c),Sofa(sit,c),w(w) //需要在汇聚子类中显性调用公共基类的有参构造函数
    {
        cout << "沙发床的有参构造" << endl;
    }
};

int main()
{
//    Sofa_Bed s;
    Sofa_Bed s1("可坐","可躺",123,"pink");
    return 0;
}

多态

类的三大属性:封装、继承、多态
静态多态(函数重载)、动态多态(运行时)

  • 多态:一种形式的多种状态(多态就像一个人,可以有很多角色或者行为,取决于不同情境)
  • 父类的指针或引用,指向或初始化子类对象,调用子类对父类重写的函数,进而展开子类的功能

函数重写

  • 必须有继承关系
  • 子类和父类有同名同类型的函数
  • 父类中的该函数必须是虚函数

虚函数

  • 在函数前加上virtual---->该函数是虚函数
  • 虚函数满足虚继承,也就是说父类中该函数时虚函数,继承到子类中,该函数依旧是虚函数,如果子类再被继承,"孙类"中该函数还是虚函数

代码示例

#include <iostream>

using namespace std;

// 封装 周 这个类
class Zhou
{
private:
    string name;
    int age;
public:
    //无参构造
    Zhou() {}


    //有参构造函数
    Zhou(string n, int a):name(n),age(a)
    {}


    //
    virtual void speek()  //表示该函数是虚函数
    {
        cout << "阿巴阿巴。。" << endl;
    }
};


//封装 周老师  类,继承于周类
class Teacher:public Zhou
{
private:
    int id;


public:
    //无参构造
    Teacher() {}


    //有参构造
    Teacher(string n, int a, int d):Zhou(n,a),id(d)
    {}


    //
    void speek()
    {
        cout << "看我,上多态,认真听讲" << endl;
    }
};


//封装 游戏玩家 类 继承于Zhou类
class Player:public Zhou
{
private:
    string game;
public:
    //。。
    Player() {}


    //有参构造
    Player(string name, int age, string g):Zhou(name,age),game(g)
    {}


    //
    void speek()
    {
        cout << "稳住,我们能赢" << endl;
    }
};


int main()
{
    Teacher t("zhangsan",34,1001);
    Zhou *p; //父类的指针
    p = &t;  //父类的指针,指向子类对象  相当于承当老师这个角色
    p->speek();   // 上课


    Player g("lisi",45,"王者");

    p = &g; //此时是游戏玩家这个角色
    p->speek();


    return 0;
}

赋值兼容规则

父类的指针或引用,指向或初始化子类的对象
在这里插入图片描述

多态中函数重写的原理

  • 类中有虚函数时,类中就会有一个虚指针,虚指针也满足继承
  • 虚指针在类的最前面,虚指针指向了一个虚函数表,虚函数表里记录了虚函数,包括子类对父类重写的函数
  • 虚指针和虚函数表是实现多态的重要机制
    在这里插入图片描述

虚机构函数

虚析构函数用来解决父类指针指向子类时,父类指针释放,导致子类自拓展的空间没有得到释放

格式

virtual 析构函数
{}

代码示例

#include <iostream>

using namespace std;

//封装 人 类
class Person
{
private:
    string name;
public:
    //
    Person() {}

    //有参构造函数
    Person(string n):name(n)
    {
    }

    virtual ~Person()  //虚析构函数  满足继承
    {
        cout << "Person::析构函数"  << endl;
    }
};

//封装 学生  继承于人
class Stu:public Person
{
private:
    int id;
public:
    //
    Stu(){}

    //有参构造
    Stu(string n , int i):Person(n),id(i)
    {}
    ~Stu()
    {
        cout << "Stu::析构函数" << endl;
    }
};

int main()
{

    Person *p = new Stu("张三",1001);
    delete p;   //如果没有虚析构函数,进行释放p是,子类自己拓展的空间就没有释放--内存泄漏

    return 0;
}

纯虚函数

当父类中虚函数被子类用来重写,且没有定义的意义,这个时候,一般把父类中的虚函数设置成纯虚函数

格式

virtual 函数返回值类型 函数名(形参列表) = 0; //纯虚函数

抽象类

抽象类一般是用来被继承的,它不能实例化出具体的一个对象,抽象类中至少有一个纯虚函数
如果子类没有对父类的纯虚函数重写,那么子类也是抽象类,不能实例化对象

#include <iostream>

using namespace std;

//..
class A  //抽象类
{
private:
    int a;
public:
    A() {}
    virtual void show() = 0;  //纯虚函数    
};


class B:public A
{
public:
    B() {}    
    void show()  //如果子类没有对父类的纯虚函数重写,那么子类也是抽象类,不能实例化一个对象
    {}
};


int main()
{
    B b;
    return 0;
}

模板

  • 模板是一个通用的摸具。大大提高了代码的复用性
  • C++的另外一个编程思想—>泛式编程:主要利用的技术就是模板
  • C++的两个重要模板机制:函数模板和类模板

模板特点

  • 模板不能直接使用,只是一个框架
  • 模板不是万能的

函数模板

作用

建立一个通用的函数,其返回值类型或者形参类型不具体制定,用一个虚拟的类型来代替

格式

template <typename T>
函数的声明或定义

template ----->表示开始创建模板
typename -->表明后面的符号是数据类型,typename 也可以用class代替
T ----->表示数据类型,可以其他符号代替

代码示例

#include <iostream>


using namespace std;


//创建函数模板
template <typename T>
void fun(T &a, T &b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}




//void fun(int &a, int &b)
//{
//    int temp;
//    temp = a;
//    a = b;
//    b = temp;


//}
//void fun(double &a, double &b)
//{
//    double temp;
//    temp = a;
//    a = b;
//    b = temp;
//}


//void fun(char &a, char &b)
//{
//    char temp;
//    temp = a;
//    a = b;
//    b = temp;
//}


int main()
{
    int a = 10, b = 20;
    fun(a,b);
    cout << a << "  " << b << endl;


    double c = 1.3, d = 1.4;
    fun(c, d);
    cout << c <<  "  " <<  d << endl;
    return 0;
}

练习

以下是一个简单的比喻,将多态概念与生活中的实际情况相联系:
比喻:动物园的讲解员和动物表演
想象一下你去了一家动物园,看到了许多不同种类的动物,如狮子、大象、猴子等。现在,动物园里有一位讲解员,他会为每种动物表演做简单的介绍。
在这个场景中,我们可以将动物比作是不同的类,而每种动物表演则是类中的函数。而讲解员则是一个基类,他可以根据每种动物的特点和表演,进行相应的介绍。
具体过程如下:
定义一个基类 Animal,其中有一个虚函数 perform(),用于在子类中实现不同的表演行为。

#include <iostream>

using namespace std;

//定义一个基类
class Animal
{
public:
    void virtual perform() = 0;
};
class Lion:public Animal
{
public:
    void perform()
    {
        cout << "狮子表演" << endl;
    }
};
class Elephant:public Animal
{
public:
    void perform()
    {
        cout << "大象表演" << endl;
    }
};
class Monkey:public Animal
{
public:
    void perform()
    {
        cout << "猴子表演" << endl;
    }
};
int main()
{
    Animal *p = new Monkey;
    p->perform();
    return 0;
}

2.用函数模板实现不同数据类型的交换功能。

#include <iostream>

using namespace std;
template <typename T>
void fun(T &a,T &b)
{
    T temp;
    temp = a;
    a = b;
    b = temp;
}
int main()
{
    int a = 10,b = 99;
    fun(a,b);
    cout << a << b << endl;
    double c = 9.9,d = 1.11;
    fun(c,d);
    cout << c << d << endl;
    return 0;
}

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

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

相关文章

NodeJS的简介以及下载和安装

本章节会带大家下载并安装NodeJs 以及简单的入门&#xff0c;配有超详细的图片&#xff0c;一步步带大家进行下载与安装 NodeJs简介关于前端与后端Node是什么&#xff1f;为什么要学习NodeNodeJS的优点&#xff1a; NodeJS的下载与安装NodeJS的下载&#xff1a; NodeJS的快速入…

Android Looper Handler 机制浅析

最近想写个播放器demo&#xff0c;里面要用到 Looper Handler&#xff0c;看了很多资料都没能理解透彻&#xff0c;于是决定自己看看相关的源码&#xff0c;并在此记录心得体会&#xff0c;希望能够帮助到有需要的人。 本文会以 猜想 log验证 的方式来学习 Android Looper Ha…

Kafka3.0.0版本——Follower故障处理细节原理

目录 一、服务器信息二、服务器基本信息及相关概念2.1、服务器基本信息2.2、LEO的概念2.3、HW的概念 三、Follower故障处理细节 一、服务器信息 三台服务器 原始服务器名称原始服务器ip节点centos7虚拟机1192.168.136.27broker0centos7虚拟机2192.168.136.28broker1centos7虚拟…

数组(个人学习笔记黑马学习)

一维数组 1、定义方式 #include <iostream> using namespace std;int main() {//三种定义方式//1.int arr[5];arr[0] 10;arr[1] 20;arr[2] 30;arr[3] 40;arr[4] 50;//访问数据元素/*cout << arr[0] << endl;cout << arr[1] << endl;cout &l…

工地扬尘监测系统 yolo

工地扬尘监测系统算法能够通过yolo网络框架模型&#xff0c;工地扬尘监测系统算法自动对区域的扬尘、粉尘颗粒进行实时监测识别&#xff0c;并及时进行预警&#xff0c;有效防止扬尘污染。Yolo意思是You Only Look Once&#xff0c;它并没有真正的去掉候选区域&#xff0c;而是…

nginx的安装和全局配置

目录 何为nginx Nginx 功能介绍 基础特性 nginx架构 Nginx 进程结构 nginx 模块 模块分类 使用方式 编译安装nginx 第一步&#xff1a;获取安装包 第二步&#xff1a;安装依赖 第三步&#xff1a;创建用户nginx 第四步&#xff1a;解压安装包 第五步&#xff1a;编译…

DSP_TMS320F28377D_算法加速方法1_拷贝程序到RAM运行

TI C2000系列的DSP芯片算力有限&#xff0c;用于来控制有时候常会出现控制程序无法实现实时运行的情况&#xff0c;因此从本文开始&#xff0c;将陆续推出几篇DSP算法加速的方法 此方法只需要添加一行代码和一个预定义&#xff0c;即可达到算法整体加速的目的。先声明本文是讲的…

jeecgboot的online在线开发高级版【伸手党福利】

本文章在于充分发挥jeecgboot的在线开发功能&#xff0c;将平时开发80%以上的工作量全部收束 目录 树形结构使用固定值替换id从其他表中引用字段输入和搜索&#xff08;下拉搜索&#xff09;输入用户、单位等框架内固有值主附表 树形结构 是否树》是 自动生成pid&#xff0c;…

oracle19c-图形安装(centos7)

目录 一.环境准备1.关闭防火墙2.关闭SELINUX3.配置本地yum源4.安装ORACLE先决条件的软件包5.修改LINUX的内核文件6.添加下列参数到/etc/security/limits.conf7.添加下列条目到/etc/pam.d/login8.环境变量中添加下列语句9.创建文件目录和相应的用户10.配置oracle用户的环境变量1…

paddle 1-高级

目录 为什么要精通深度学习的高级内容 高级内容包含哪些武器 1. 模型资源 2. 设计思想与二次研发 3. 工业部署 4. 飞桨全流程研发工具 5. 行业应用与项目案例 飞桨开源组件使用场景概览 框架和全流程工具 1. 模型训练组件 2. 模型部署组件 3. 其他全研发流程的辅助…

别考,快跑!

写在前面 我一般不直接推荐院校&#xff0c;但是根据这所院校的23情况来看&#xff0c;复试一票否决率太高了&#xff0c;我看的怀疑人生&#xff0c;所以纠结很久&#xff0c;还是说出我自己的想法&#xff1a;我不推荐我的粉丝考这所学校&#xff0c;我更希望大家考研上岸是…

PMP教材改版难不难?考生应如何应对?

有很多同学都在担心PMP教材改版后&#xff0c;考试会不会增加难度&#xff0c;今天胖圆给大家详细讲解一下&#xff0c;在PMP改革背景下&#xff0c;考生应该怎样应对&#xff1f; 新教材的改动意味着8月以后的考试都将 《PMBOK指南》第七版的内容纳入考试范围&#xff0c;其中…

【USRP】调制解调系列7:GMSK、MSK、基于labview的实现

MSK 在数字调制中&#xff0c;最小频移键控&#xff08;Minimum-Shift Keying&#xff0c;缩写&#xff1a;MSK&#xff09;是一种连续相位的频移键控方式&#xff0c;在1950年代末和1960年代产生。与偏移四相相移键控&#xff08;OQPSK&#xff09;类似&#xff0c;MSK同样将…

JavaScript关于函数的小挑战

题目 回到两个体操队&#xff0c;即海豚队和考拉队! 有一个新的体操项目&#xff0c;它的工作方式不同。 每队比赛3次&#xff0c;然后计算3次得分的平均值&#xff08;所以每队有一个平均分&#xff09;。 只有当一个团队的平均分至少是另一个团队的两倍时才会获胜。否则&…

openGauss学习笔记-54 openGauss 高级特性-MOT

文章目录 openGauss学习笔记-54 openGauss 高级特性-MOT54.1 MOT特性及价值54.2 MOT关键技术54.3 MOT应用场景54.4 不支持的数据类型54.5 使用MOT54.6 将磁盘表转换为MOT openGauss学习笔记-54 openGauss 高级特性-MOT openGauss引入了MOT&#xff08;Memory-Optimized Table&…

Glide分析和总结

1. Glide概述 Glide是一款图片处理的框架&#xff0c;从框架设计的角度出发&#xff0c;最基本要实现的就是 加载图片 和 展示。 它把一个图片请求封装成一个Request对象&#xff0c;里面有开启、暂停、关闭、清除网络请求、以及载体生命周期的监听等操作。然后通过RequestBu…

导入表解析与IATHook

IAT&#xff1a;导入地址表// PE文件解析.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <Windows.h>//函数向前声明 DWORD RvaToFoa(DWORD dwRva, const char* szBuff…

【人工智能】—_贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

文章目录 频率学派 vs. 贝叶斯学派贝叶斯学派Probability&#xff08;概率&#xff09;:独立性/条件独立性&#xff1a;Probability Theory&#xff08;概率论&#xff09;:Graphical models &#xff08;概率图模型&#xff09;什么是图模型&#xff08;Graphical Models&…

【Vue3】transition-group 过渡列表

1. 基本使用 transition-group 和 transition 的用法基本上是一样的。 <template><div class"content"><button click"add">add</button><button click"pop">pop</button><div class"wrap"&…

Michael.W基于Foundry精读Openzeppelin第33期——EIP712.sol

Michael.W基于Foundry精读Openzeppelin第33期——EIP712.sol 0. 版本0.1 EIP712.sol 1. 目标合约2. 代码精读2.1 constructor(string memory name, string memory version)2.2 _domainSeparatorV4()2.3 _hashTypedDataV4(bytes32 structHash) 0. 版本 [openzeppelin]&#xff…