C++基础讲解第五期(继承、多继承、虚继承、向上转型、同名函数)

news2024/9/21 0:44:16

C++基础讲解第五期 代码中也有对应知识注释,别忘看,一起学习!

  • 一、继承(接第四期)
    • 1. const修饰成员函数
    • 2. 同名函数
    • 3. 继承中的static关键字
    • 4. 继承中类型兼容性原则
    • 5. 多继承(使用参数初始化列表)
      • (1)多继承的概念
      • (2)多继承的语法
      • (3)多继承中的二义性问题
      • (4)虚继承
    • 6. 向上转型
  • 二、练习

一、继承(接第四期)

1. const修饰成员函数

总结:
常成员函数修饰只读变量,在定义和声明处都需要加上const

#include<iostream>
using namespace std;

class Student
{
    private:
        char *m_name;
        int m_age;
        float m_score;

    public:
        Student(char* name, int age, float socre);
        void show();
        char* Getname() const;
        int Getage() const;
        float Getscore() const;
};

Student::Student(char* name, int age, float socre):m_name(name),m_age(age),m_score(socre)
{

}

void Student::show()
{
    cout<<this->m_name<<"的年龄是:"<<this->m_age<<"分数是:"<<this->m_score<<endl;
}

char* Student::Getname() const
{
    return m_name;
}

int Student::Getage() const //常成员函数必须声明和定义处都需要加上const关键字
{
    return m_age;
}

float Student::Getscore() const 
{
    //m_socre++ 常成员函数修饰只读变量
    return m_score;
}

int main()
{
    Student s1("小明",18,100);
    s1.show();
    return 0;
}

2. 同名函数

总结:

  1. 当基类与派生类有同名的成员函数时,当我们使用派生类对象去调用同名函数时, 默认调用派生类的同名成员函数。
  2. 想要使用基类的同名函数,需要加上作用域,显式调用。
#include<iostream>
using namespace std;

class TestA
{
    private:
        int m_a;

    public:
        void show()
        {
            cout <<"This is TetsA show"<< endl;
        }
};

class TestB:public TestA
{
    private:
        int m_a;

    public:
        void show()
        {
            cout <<"This is TetsB show"<< endl;
        }
};

int main()
{
    TestB tb;
    tb.show();//当同名时,默认调用派生类成员函数
    tb.TestA::show();//当同名时,想要调用基类同名函数,需要加上作用域,显式调用
    return 0; 
}

结果:
This is TetsB show
This is TetsA show

3. 继承中的static关键字

如果在基类中定义了静态成员变量,则该静态成员变量将被所有派生类共享

#include<iostream>
using namespace std;

class Person
{
    private:

    public:
        static int count; //静态成员变量,所有对象共享一个静态成员变量
        Person()
        {
            count ++;
        }
};
int Person::count = 0; //静态变量只能在类外初始化

class Student:public Person//继承了Person类中的count,并于Person类共享
{
    
};

int main()
{
    Student s1;
    Student s2;
    Person p1;
    Person p2;

    cout<<Person::count<<endl; //派生类和基类共享一个静态成员变量
    cout<<Student::count<<endl;
    
    return 0;
}
结果:
4
4

4. 继承中类型兼容性原则

概念:
类型兼容性原则是指在需要基类的地方,对象的地方,都可以使用公有派生类的对象代替,通过公有继承,派生类得到了基类中除构造函数析构函数的所有成员,这样,公有派生类就具备了基类所有的功能。
凡是基类可以完成的事,公有派生类都可以解决

类型兼容性:

  1. 子类对象可以当作父类对象使用,子类对象是一种特殊的父类。
    父类能做的事情,子类对象都可以做。
  2. 父类指针可以直接指向子类对象
    基类引用直接引用派生类对象

代码说明:

#include<iostream>
using namespace std;

class Parent
{
    public:
        Parent()
        {
            cout<<"parent 无参构造函数"<<endl;
        }

        Parent(const Parent &p)
        {
            cout<<"parent 的拷贝构造函数"<<endl;
            this->m_a = p.m_a;
            this->m_b = p.m_b;
        }

        void SetAB(int a,int b)
        {
            this->m_a = a;
            this->m_b = b;
        }

        void printP()
        {
            cout<<"m_a= "<< m_a <<endl;
            cout<<"m_b= "<< m_b <<endl;
        }
    protected:
        int m_a;
        int m_b;
};

class Child:public Parent
{
    private:
        int m_c;

    public:
        void SetC(int c)
        {
            this->m_c = c;
        }

        void printC()
        {
            cout << "m_c= " << m_c << endl;
        }
};

int main()
{
    Parent p1;
    p1.SetAB(1,2);
    p1.printP();

    Child c;
    c.SetAB(10,20);
    c.SetC(30);
    c.printC();

    Parent *p = &c; //基类指针可以指向派生类对象,但是派生类指针不能指向基类对象,这里不会调用Parent的构造函数,因为只是定义了Parent的指针,没有为其分配空间
    p->SetAB(7,8);
    //p->SetC(7);  父类指针不能调用派生类成员

    //Child *pc = &p1 子类指针不能指向基类对象

    Parent &p2 = c;//基类引用可以直接引用派生类对象
    p2.printP();

    Parent p3(c); //派生类对象可以用来初始化基类对象
    p3.printP();
    return 0;
}

画图解释
Parent基类只在堆上开发了m_a,m_b的空间,而Child派生类,继承了Parent类,Child派生类包含m_a, m_b, m_c, 比基类多了一个m_c,他们的首地址都是一样的。

  1. 当基类指针指向派生类时, 它只有m_a,m_b的访问权限,不会访问到m_c. 所有基类指针可以指向派生类对象。
  2. 当派生类指针指向基类对象时,它可以访问m_c, 但是基类对象没有开发m_c的空间,所有会发生越界。所以派生类指针不能指向基类对象。
  3. 引用同理
  4. 派生类对象可以初始化基类对象,同理。

在这里插入图片描述

5. 多继承(使用参数初始化列表)

(1)多继承的概念

派生类中只有一个基类,称为单继承
除此之外C++支持多继承,一个派生类可以有多个基类
多继承容易使代码逻辑复杂,思维混乱,一直备受争议,在中小型项目中很少使用,JAVA/C#/PHP干脆取消了多继承

(2)多继承的语法

#include<iostream>
using namespace std;

class Airplane
{
    protected:
        int high;
    public:
        Airplane(int h) : high(h)
        {
            cout<<"飞机的构造函数"<<endl;
            //high = 100;
        }

        void show()
        {
            cout<<"飞机的高度:"<< high << endl;
        }
};

class Ship
{
    protected:
        int speed;
    public:
        Ship(int s) : speed(s)
        {
            cout<<"轮船的构造函数"<<endl;
            //speed = 50;
        }

        void show()
        {
            cout<<"轮船的速度:"<< speed << endl;
        }
};

class WaterPlane :public Airplane, public Ship
{
    public:
        WaterPlane(int h, int s, int d): Airplane(h), Ship(s), deep(d) //参数初始化列表使用,还有部分使用在第三期
        {
            cout<<"水上飞机的构造函数"<<endl;
        }

    private:
        int deep;
};


int main()
{
    WaterPlane w(100, 50, 10);//先构造基类,再构造派生类,基类构造的顺序与继承的顺序相同
    w.Airplane::show();
    return 0;
}

(3)多继承中的二义性问题

#include<iostream>
using namespace std;

class TestA
{
    public:
        int a;
};

class TestB : public TestA
{
    public:
        int b;
};

class TestC : public TestA
{
    public:
        int c;
};

class TestD : public TestB , public TestC
{
    public:
        int d;
};

int main()
{
    TestD td;
    cout<<sizeof(td) <<endl;
    //td.a;产生了二义性,不知道是哪个B类还是C类里的a
    td.TestB::a;//加上作用域即可

    return 0;
}

在这里插入图片描述

(4)虚继承

虚继承的目的:是让某个类做出声明,承诺愿意共享它的基类,这个被共享的基类就被称为虚基类,在这种机制下,不论虚基类在继承体系中出现多少次,在派生类中只会包含一份虚基类的成员

#include<iostream>
using namespace std;

class TestA
{
    public:
        int a;
};

class TestB :virtual public TestA
{
    public:
        int b;
};

class TestC :virtual public TestA
{
    public:
        int c;
};

class TestD : public TestB , public TestC
{
    public:
        int d;
};

int main()
{
    TestD td;
    cout<<sizeof(td) <<endl;
    
    cout<<&td<<endl;
    cout<<&td.a<<endl;
    cout<<&td.b<<endl;
    cout<<&td.c<<endl;
    cout<<&td.d<<endl;

    return 0;
}
结果:
40
0x7ffd616a7600
0x7ffd616a7620
0x7ffd616a7608
0x7ffd616a7618
0x7ffd616a761c

需要解释下,为什么td占40个字节
因为虚基类带有虚基类指针
td的首地址是00,后面接有虚基类指针8个字节,然后是b,占4个字节
然后再接虚基类指针占8个字节,然后是c,占4个字节,然后是d,占4个字节
然后是a,占4个字节,再带上结构体所要求的字节对齐,在b后面补了4个字节,和a后面补了4个字节

对于虚基类的作用在于,a只存在了一次,如果像多继承二义性所写,应该有两个a,因为B类继承了A类中的a,C类也继承了A类中的a,因为是虚继承,所以只有一个a

画图说明
在这里插入图片描述

6. 向上转型

只能将派生类对象赋值给基类对象,派生类引用赋值给基类引用,派生类指针赋值给基类指针 ,这就叫向上转型。

二、练习

使用C++实现单链表的功能(头插法)

我就写个简单的实现

#include<iostream>
using namespace std;

class HNode
{
    public:
        int num;
        HNode *next;
};

class HLink
{
    HNode *head;
    int length;

    public:
        void CreateLink();

        HNode* CreateNode();

        void insertNode(HNode* newNode);

        void disPlay();
};


void HLink::CreateLink()
{

    head = new HNode;
    head->num = 0;
    head->next = nullptr;
}

HNode* HLink::CreateNode()
{
    HNode* node = new HNode;
    if(node == nullptr)
    {
        cout<<"create node error" <<endl;
    }
    return node;
}

void HLink::insertNode(HNode* newNode)
{
    if(head->next == nullptr)
    {
        head->next = newNode;
        newNode->next = nullptr;
    }
    else
    {
        newNode->next = head->next;
        head->next = newNode;
    }
}

void HLink::disPlay()
{
    HNode *ptr;
    ptr = head->next;
    while(ptr == nullptr)
    {
        cout<<ptr->num<<endl;
        ptr = ptr->next;
    }
}

int main()
{
    HLink link;
    link.CreateLink();
    
    for(int i = 0; i < 5; i++)
    {
        HNode *node = link.CreateNode();
        node->num = i;
        link.insertNode(node);
    }

    link.disPlay();

    return 0;
}

总结在使用C++时,会有的困扰
当我使用C习惯之后,在写链表的时候会去考虑二级指针,以及链表变了怎么才能反映出来。
其实,在我们实现类的成员函数时,在成员函数里面去对成员变量进行改变,当我们去实例化这个类的时候,这个成员函数和成员变量才真正的产出,比如上面的insertNode(),在它的里面直接用了成员变量head,但是当我实例化出list的时候,调用list.insertNode()的时候,使用的就是list对象的head,改变它,就时有效的,生命周期一直到这个对象销毁。不像C中,生命周期是它的本函数内,所以要传二级指针。

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

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

相关文章

SDN控制器三平面(软件定义网络、OOB)

又名 软件定义网络 软件定义网络SDN(Software Defined Network)是由美国斯坦福大学CLean Slate研究组提出的一种新型网络创新架构,可通过软件编程的形式定义和控制网络,其控制平面和转发平面分离及开放性可编程的特点,被认为是网络领域的一场革命,为新型互联网体系结…

Gitlab----基于Kubernetes安装Gitlab

【原文链接】Gitlab----基于Kubernetes安装Gitlab &#xff08;1&#xff09;创建命名空间gitlba kubectl create namespace gitlab&#xff08;2&#xff09;然后创建Gitlab用的PVC&#xff0c;编写yaml配置文件如下 kind: PersistentVolumeClaim apiVersion: v1 metadata:…

002 - STM32固件库GPIO(一)操作LED灯

STM32固件库操作LED灯 1、创建板级支持包文件 在User目录下创建LED文件夹&#xff0c;包含bsp_led.c及头文件bsp_led.h&#xff0c;之后在Keil中将bsp_led.c加入 2、程序代码 bsp_led.c #include ".\LED\bsp_led.h" //相对路径访问&#xff0c;可以在工程配置内…

LeetCode 199. 二叉树的右视图

199. 二叉树的右视图 描述 给定一个二叉树的 根节点 root&#xff0c;想象自己站在它的右侧&#xff0c;按照从顶部到底部的顺序&#xff0c;返回从右侧所能看到的节点值。 示例 示例1 输入&#xff1a;[1,2,3,null,5,null,4] 输出&#xff1a;[1,3,4] 示例2 输入&#xf…

【笔试强训选择题】Day14.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录…

Linux环境搭建与历史

目录 前言 1.操作系统是什么 2. 为什么我们要选择Linux 3.Linux操作系统发展历史 4.企业应用现状 Linux在服务器领域的发展 Linux在桌面领域的发展 Linux在移动嵌入式领域的发展 Linux在云计算/大数据领域的发展 5.发行版本 6. Linux环境配置 6.1 Linux搭建的方式 …

程序员挣够了钱,到中年你还害怕失业吗?

最近一刷知乎全部都是大龄程序员失业危机 真的有这么可怕吗&#xff1f; 程序员35岁就真的到了瓶颈期&#xff1f; 我不这么认为 挣够了钱&#xff0c;当然不可怕&#xff0c;问题是没挣够啊~&#xff01;&#xff01;&#xff01; 按题主的算法是&#xff0c;大城市薪资1…

C++基础STL-map容器

map容器介绍&#xff1a; 作为关联式容器的一种&#xff0c;map 容器存储的都是 pair 对象&#xff0c;也就是用 pair 类模板创建的键值对。其中&#xff0c;各个键值对的键和值可以是任意数据类型&#xff0c;包括 C基本数据类型&#xff08;int、double 等&#xff0…

快速入门matlab——变量练习

学习目标&#xff1a;1.掌握matlab编程中最常用的几种变量类型 2.对变量类型的属性有所熟悉&#xff0c;不要求记忆&#xff0c;知道了解即可 3.要求熟练运用这几种变量类型创建自己的变量 clear all; % 清除Workspace中的所有…

服务网格:优化微服务通信与保障系统安全性的架构利器

文章目录 服务网格&#xff1a;优化微服务通信与保障系统安全性的架构利器1. 什么是服务网格&#xff1f;微服务架构概述&#xff1a;服务网格的定义和作用&#xff1a; 2. 服务网格的特点&#xff1a;透明性&#xff1a;无需修改应用代码服务发现与负载均衡&#xff1a;动态发…

GPT3.5 VS GPT-4写领导讲话稿,谁是最强笔杆子?

正文共 1240 字&#xff0c;阅读大约需要 5 分钟 文秘/公务员必备技巧&#xff0c;您将在5分钟后获得以下超能力&#xff1a; 快速生成领导讲话稿 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●…

redis持久化配置

回顾&#xff1a;rpcbind111 nfs2049 redis高可用高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%&#xff0c;99.99%&#xff0c;99.999%等&#xff09; 还要考虑提供主从分离&#xff0c;快速容灾技术&#…

在 React 中使用 highlight.js 和 Clipboard.js 实现代码块和复制功能

参考链接&#xff1a;https://blog.csdn.net/huangjuan0229/article/details/130319050 在前端开发中&#xff0c;代码块高亮和复制功能是十分常见的需求。而在 React 中&#xff0c;常用的代码高亮库是 highlight.js&#xff0c;常用的复制库是 Clipboard.js。本篇文章将介绍…

新版 钥证书模式加签模式支付宝官方支付对接·错误代码 missing-signature 错误原因: 缺少签名参数 解决方案

新版密钥证书加签模式支付宝官方支付对接错误代码 missing-signature 错误原因: 缺少签名参数 解决方案 大家好我是优雅草伊凡&#xff0c;最近处理的问题特别多每天忙得不可开交&#xff0c;但是我相信后续要写的内容和记录下的问题是越来越多了&#xff0c;其他同事忙着开发…

10-02 单元化架构核心问题与概念

单元化架构产生的原因 应对增长 传统架构无法处理日益增长的互联网用户需求扩容 需要新架构更近一部提升了系统的扩展能力系统稳定性 新架构需要高可用、相对独立和故障隔离使整体系统更稳定灰度发布 系统和组件都纳入版本管理&#xff0c;按需部署进行灰度发布 核心问题 应…

成都待慕电商:抖音百亿现金扶持商家

新京报贝壳财经讯5月16日&#xff0c;抖音电商在广州举办第三届生态大会。抖音电商总裁魏雯雯透露&#xff0c;过去一年&#xff0c;抖音电商GMV&#xff08;商品交易总额&#xff09;同比增长80%&#xff0c;其中&#xff0c;货架场景GMV占比达30%&#xff0c;平台售出超300亿…

公司新招了个字节拿36K的人,让我见识到了什么才是测试扛把子......

5年测试&#xff0c;应该是能达到资深测试的水准&#xff0c;即不仅能熟练地开发业务&#xff0c;而且还能熟悉项目开发&#xff0c;测试&#xff0c;调试和发布的流程&#xff0c;而且还应该能全面掌握数据库等方面的技能&#xff0c;如果技能再高些的话&#xff0c;甚至熟悉分…

【目标检测】基于yolov5的交通标志检测和识别(可识别58种类别,附代码和数据集)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 文末附项目代码和数据集,请看检测效果: 1. 介绍 YOLOv5是一种用于目标检测的深度学习算法,它能够在高速和高精度的情…

Spring中的@Value注解详解

Spring中的Value注解详解 概述 本文配置文件为yml文件 在使用spring框架的项目中&#xff0c;Value是经常使用的注解之一。其功能是将与配置文件中的键对应的值分配给其带注解的属性。在日常使用中&#xff0c;我们常用的功能相对简单。本文使您系统地了解Value的用法。 Value…

人工智能的新时代:讯飞星火大模型Vs ChatGPT

近日&#xff0c;科大讯飞召开了星火认知大模型成果发布会&#xff0c;会上表示讯飞星火大模型将突破开放式问答&#xff0c;对标ChatGPT&#xff0c;在中文能力上超过ChatGPT&#xff0c;在英文能力上与ChatGPT相当。对此&#xff0c;你怎么看&#xff1f; 一、你有使用过这种…