05-C++ 类和对象-继承

news2024/11/25 20:35:35

类与对象-03

继承与派生

1. 继承的概念

c++最重要的特征是代码重用,通过继承机制可以利用已有的数据类型,来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员。

一个 B 类继承于 A 类,或称从类 A 派生类 B。这样的话,类 A 成为基类(父类),类 B 成为派生类(子类)。

派生类中的成员,包含两大部分:

  • 一类是从基类继承过来的,
  • 一类是自己增加的成员。

从基类继承过过来的表现其共性,而新增的成员体现了其个性。

2. 继承的优点

1)减少重复的代码,减轻程序整体的体量。

2)继承的好处,可以将共性的内容封装成一个基类(父类),遇到专项业务时,可以扩展基类变为一个新类,在新类中重点扩展功能。

3. 继承的语法

class 子类:继承方式 父类名
{ 
	子类新增自己的数据和方法;
};

4. 继承方式

  • public(推荐):子类将 原封不动的继 承父类成员,但是 不能直接访问 父类 私有的成员

  • protected:子类 继承到 父类的成员 转换为 protected修饰的成员,但是 不能直接访问 父类 私有的成员

  • private:子类将 继承到所有父类成员 转换为 私有的成员,但是 不能直接访问 父类 私有的成员

在这里插入图片描述

示例1:public

#include <iostream>
using namespace std;
class A
{
public:
    int a;
    void testA()
    {
        cout << "testA" << endl;
    }
};
class B:public A
{
};
int main(int argc, char *argv[])
{
    B b;
    b.a = 10;
    b.testA();  //testA
    return 0;
}

示例2:protected ,若是protected继承,父类中的public将变为 protected,只能在当前类或子类中使用。

#include <iostream>
using namespace std;
class A
{
public:
    int a;
    void testA()
    {
        cout << "testA" << endl;
    }
};
class B:protected A
{
public:
    void testB()
    {
        cout << a << endl;
        testA();
    }
};
int main(int argc, char *argv[])
{
    B b;
    //此时是protected继承,父类中的public将变为 protected,只能在当前类或子类中使,
    //所以下面报错
    //b.a = 10;//报错
    //b.testA();//报错
    b.testB(); //93 testA
    return 0;
}

5. 注意

  1. 子类可以继承父类所有成员,但是父类私有成员不可访问
  2. 子类 不能继承父类构造函数拷贝构造析构函数,但是子类中可以调用
  3. 子类可以多继承
  4. 子类在 创建对象 时会 调用父类构造函数,如果 没有明确 写出 调用的父类构造 函数,默认调用 父类无参构造 ,此时如果父类 没有无参构造,程序报错
  5. 子类调用父类构造函数,在 子类构造函数后使用初始化列表方式调用父类构造函数
  6. operator=不能被继承

示例:

#include <iostream>
#include <cstring>
using namespace std;
class Anim{
private:
    char type[50];
    int age;
    char sex[10];
public:
    Anim(){
        cout << "Anim无参构造" << endl;
    }
    Anim(char *type,char *sex,int age)
    {
        strcpy(this->type,type);
        strcpy(this->sex,sex);
        this->age = age;
        cout << "Anim有参构造" << endl;
    }
    Anim(const Anim& anim)
    {
        strcpy(this->type,anim.type);
        strcpy(this->sex,anim.sex);
        this->age = anim.age;
    }
    void setType(char *type)
    {
        strcpy(this->type,type);
    }
    char* getType()
    {
        return type;
    }
    void setSex(char *sex)
    {
        strcpy(this->sex,sex);
    }
    char* getSex()
    {
        return sex;
    }
    void setAge(int age)
    {
        this->age = age;
    }
    int getAge()
    {
        return age;
    }
};
class A{

};
//3、子类可以多继承
class Yang:public Anim,public A
{
public:
    Yang(){}

    //5、初始化列表调用父类构造函数
    Yang(char *type,char *sex,int age):Anim(type,sex,age){

    }
};

int main(int argc, char *argv[])
{
    //Yang y;
    //1、子类可以继承父类所有成员,但是父类私有成员不可访问
    //y.age;
    //2、子类不能继承父类的构造函数,拷贝构造,析构函数
    //Yang y("绵羊","公",18); //Anim有参构造
    //4、创建子类对象时会调用父类构造函数
    //默认调用父类无参构造
    //Yang y;
    //如果子类构造函数在初始化列表中指定调用父类的构造函数
    //那么就不会默认调用父类无参构造
    //Yang y("山羊","公",3);
    //如果父类中没有无参构造,子类没有明确写出调用父类构造函数
    //此时子类将调用父类无参构造,但是父类没有无参构造
    //程序报错
    Yang y;
    return 0;
}

6. 构造与析构的执行顺序

继承中的构造和析构:

  • 子类对象在创建时会首先调用父类的构造函数
  • 父类构造函数执行完毕后,才会调用子类的构造函数
  • 当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数
  • 析构函数调用顺序和构造函数相反

在这里插入图片描述

示例1:子类继承父类

#include <iostream>

using namespace std;
class A{
public:
    A(){
        cout << "A的构造函数" << endl;
    }
    ~A(){
        cout << "A的析构函数" << endl;
    }
};
class B:public A{
public:
    B(){
        cout << "B的构造函数" << endl;
    }
    ~B(){
        cout << "B的析构函数" << endl;
    }
};
int main(int argc, char *argv[])
{
    B b;
    return 0;
}
//A的构造函数
//B的构造函数
//B的析构函数
//A的析构函数

示例2:子类继承父类,该子类中还要其他类的成员

#include <iostream>

using namespace std;
class A{
public:
    A(){
        cout << "A的构造函数" << endl;
    }
    ~A(){
        cout << "A的析构函数" << endl;
    }
};
class C{
public:
    C(){
        cout << "C的构造函数" << endl;
    }
    ~C(){
        cout << "C的析构函数" << endl;
    }
};
class B:public A{
private:
    C c;
public:
    B(){
        cout << "B的构造函数" << endl;
    }
    ~B(){
        cout << "B的析构函数" << endl;
    }
};
int main(int argc, char *argv[])
{
    B b;
    return 0;
}
//A的构造函数
//C的构造函数
//B的构造函数
//B的析构函数
//C的析构函数
//A的析构函数

7. 继承中父子类成员重名

7.1 成员变量重名

调用方式:

  • 操作子类成员变量:子类对象.成员变量名
  • 操作父类提供的成员变量:子类对象.父类名::成员变量名

示例:

#include <iostream>

using namespace std;
class Basic{
public:
    int x;
    int y;
    Basic(int x):x(x){}
};
class Son:public Basic{
public:
    int x;
    //子类构造函数中必须调用父类构造函数
    //默认调用父类无参构造
    Son(int x1, int x2):Basic(x1){
        this->x = x2;
    }
};

int main(int argc, char *argv[])
{
    Son son(10,100);
    //当父子类成员变量重名时
    //此时子类对象中有两个名称相同的变量
    //1个是继承父类的
    //1个是子类特有的
    //获取子类特有的该变量
    cout << son.x << endl;//100
    //获取父类提供的x变量
    cout << son.Basic::x << endl;//10
    //没有重名的成员直接获取
    cout << son.y << endl; //随机数
    return 0;
}
7.2 成员函数重名

概念:

  • 子类成员函数名父类成员函数名 重名,此时 子类该函数就是对父类名为该函数名的函数进行 重定义
  • 重定义
    • 继承关系中
    • 子类函数名与父类函数名相同
  • 特点:屏蔽父类该函数

调用方式:

  • 操作子类成员 函数:子类对象.成员函数名(实参列表)
  • 操作父类提供的成员函数:子类对象.父类名::成员函数名(实参列表)

示例:

#include <iostream>

using namespace std;
class Fu{
public:
    void test(){
        cout << "fu test()" << endl;
    }
    void test(int a){
        cout << "fu test(int)" << endl;
    }
    void test(int a, int b){
        cout << "fu test(int, int)" << endl;
    }
    void fun01(){
        cout << "fu fun01()" << endl;
    }
};
class Zi:public Fu{
public:
    void test(){
        cout << "zi test()" << endl;
    }
};

int main(int argc, char *argv[])
{
    Zi zi;
    //当父子类函数名重名时
    //子类调用重名函数,默认调用的是子类自己的函数
    zi.test();      //zi test()
    //子类调用继承于父类的 重名函数
    zi.Fu::test();  //fu test()
    zi.Fu::test(1);  //fu test(int)
    zi.Fu::test(10,20);  //fu test(int, int)

    //子类调用继承于父类的 非重名函数
    zi.fun01();     //fu fun01()
    return 0;
}

8. 多继承

概念:一个子类继承与 多个父类

语法:

class 子类名:继承方式1 父类1, 继承方式2 父类2, ...
{
	子类特有成员
}

父类构造顺序:

  • 子类对象创建时,按继承 编写的顺序 依次执行父类对象构造,与子类构造函数后初始化列表中的顺序无关

示例1:

#include <iostream>

using namespace std;
class A{
public:
    int x;
    A(int a):x(a){}
};
class B{
public:
    int x;
    B(int a):x(a){}
};
class C:public A,public B
{
public:
    int x;
    C(int a,int b,int c):x(a),A(b),B(c){}
};
int main(int argc, char *argv[])
{
    C c(1,2,3);
    //获取c类中的x
    cout << c.x << endl;    //1
    //获取c类中继承与A类的x
    cout << c.A::x << endl; //2
    //获取c类中继承与B类的x
    cout << c.B::x << endl; //3
    return 0;
}

示例2:

#include <iostream>

using namespace std;
class A{
    public:
    A(){
        cout << "A的构造函数" << endl;
    }
    ~A(){
        cout << "A的析构函数" << endl;
    }

};
class B{
    public:
    B(){
        cout << "B的构造函数" << endl;
    }
    ~B(){
        cout << "B的析构函数" << endl;
    }
};
class C:public B,public A{
    public:
    C():A(),B(){
        cout << "C的构造函数" << endl;
    }
    ~C(){
        cout << "C的析构函数" << endl;
    }
};
int main(int argc, char *argv[])
{
    C c;
    return 0;
}
//B的构造函数
//A的构造函数
//C的构造函数
//C的析构函数
//A的析构函数
//B的析构函数

9. 菱形继承(了解)

概念:

A的子类A1与A2

B类多继承A1与A2类

此时这种关系称为菱形继承

注意:

  • 菱形继承会导致子类用于多份祖先数据,当孙子类调用成员(函数或数据)时,会产生二义性。
  • 如:
    • A类中提供num成员变量
    • A1与A2类属于A的子类,,那么A1与A2类将各自拥有一份num
    • B作为A1与A2的子类,那么 B将拥有两个num

在这里插入图片描述

示例:

#include <iostream>
#include <cstring>
using namespace std;
class Anim{
public:
    char name[50];
    Anim(char *name)
    {
        strcpy(this->name, name);
    }
};
class Yang:public Anim{
public:
    Yang(char *name):Anim(name)
    {

    }
};
class Tuo:public Anim{
public:
    Tuo(char *name):Anim(name)
    {

    }
};
class YangTuo:public Yang, public Tuo{
public:
    YangTuo(char *name01, char *name02):Yang(name01),Tuo(name02)
    {

    }
};
int main(int argc, char *argv[])
{
    YangTuo yt("tom", "jerry");
    cout << yt.Yang::name << endl;  //tom
    cout << yt.Tuo::name << endl;   //jerry
    
    cout << &(yt.Yang::name) << endl;   //0x61fe2c
    cout << &(yt.Tuo::name) << endl;    //0x61fe5e

    return 0;
}

菱形继承类布局:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

10. 虚继承

概念:使用 virtual修饰继承关系

语法:

class 子类名:virtual 继承关系 父类名
{

};

解决问题:

解决菱形继承的调用二义性,多个类只保存一份相同数据

如:

  • A类中提供num成员变量
  • A1与A2类属于A的子类,那么A1与A2类将各自拥有一份num
  • B作为A1与A2的子类,那么B将拥有两个num,此时调用num会出现二义性
  • 所以只能虚继承使其存储一个num.

示例:

#include <iostream>
#include <cstring>
using namespace std;
class Anim{
public:
    char name[50];
    Anim(char *name)
    {
        strcpy(this->name, name);
    }
};
class Yang:virtual public Anim{
public:
    Yang(char *name):Anim(name)
    {

    }
};
class Tuo:virtual public Anim{
public:
    Tuo(char *name):Anim(name)
    {

    }
};
class YangTuo:public Yang, public Tuo{
public:
    YangTuo(char *name01, char *name02):Anim(name01),Yang(name01),Tuo(name02)
    {

    }
};

int main(int argc, char *argv[])
{
    YangTuo yt("tom", "jerry");
    cout << yt.Yang::name << endl;  //tom
    cout << yt.Tuo::name << endl;   //tom

    cout << &(yt.Yang::name) << endl;   //0x61fe5c
    cout << &(yt.Tuo::name) << endl;    //0x61fe5c
    return 0;
}

虚继承类布局:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

【操作系统】不同操作系统内核架构分析

一、内核架构与操作系统性能之间的关系的分析 1. 适用性和专业化&#xff1a; 不同的内核架构往往会有不同的设计目标和优化点。例如&#xff0c;实时操作系统&#xff08;RTOS&#xff09;和通用操作系统&#xff08;像Linux或Windows&#xff09;在设计时就有不同的重点&am…

数据结构--二叉搜索树的实现

目录 1.二叉搜索树的概念 2.二叉搜索树的操作 二叉搜索树的插入 中序遍历(常用于排序) 二叉搜索树的查找 二叉搜索树的删除 完整二叉树代码&#xff1a; 二叉搜索树的应用 key/value搜索模型整体代码 1.二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一…

【第十三课】Trie字符串统计(acwing-835 / 二维数组的含义 / c++代码)

思想 Trie树在我们之前学习树的时候简单提过一嘴。 Trie树也称为前缀树或字典树&#xff0c;是一种用于高效存储和查找字符串的数据结构。Trie树的主要思想是利用字符串之间的公共前缀来节省存储空间&#xff0c;提高查询效率。 节点表示&#xff1a;Trie树中的每个节点代表一…

[Angular] 笔记 16:模板驱动表单 - 选择框与选项

油管视频&#xff1a; Select & Option (Template Driven Forms) Select & Option 在 pokemon.ts 中新增 interface: export interface Pokemon {id: number;name: string;type: string;isCool: boolean;isStylish: boolean;acceptTerms: boolean; }// new interface…

JavaScript(简写js)常用事件举例演示

目录 1.窗口事件onblur :失去焦点onfocus:获得焦点onload:窗口加载事件onresize:窗口大小缩放事件 二、表单事件oninput &#xff1a;当文本框内容改变时 &#xff0c;立即将改变内容 输出在控制台onchange&#xff1a; 内容改变事件onclick&#xff1a;鼠标单击时触发此事件 三…

OR-NeRF论文笔记

OR-NeRF论文笔记 文章目录 OR-NeRF论文笔记论文概述Abstract1 Introduction2 Related Work3 Background4 Method4.1 Multiview Segmentation4.2 Scene Object Removal 5 ExperimentsDatasetsMetricsMultiview SegmentationScene Object Removal 6 Conclusion 论文概述 目的&am…

resnet18

ResNet18的基本含义是&#xff0c;网络的基本架构是ResNet&#xff0c;网络的深度是18层。但是这里的网络深度指的是网络的权重层&#xff0c;也就是包括池化&#xff0c;激活&#xff0c;线性层。而不包括批量化归一层&#xff0c;池化层。 transforms.RandomCrop(32, pa…

悔不该用中文作为Windows的用户名啊~

前言 汉字在中华文明已经有了几千年的历史&#xff0c;小伙伴们所使用名字更是伴随了自己一生。所以小白们在拿到自己的新电脑&#xff0c;总会想着把自己的中文名字设置为电脑的用户名&#xff0c;这样更能显示出那是自己的专属电脑&#xff01; 一开始小白也是这么想的&…

Unity中Shader裁剪空间推导(在Shader中使用)

文章目录 前言一、在Shader中使用转化矩阵1、在顶点着色器中定义转化矩阵2、用 UNITY_NEAR_CLIP_VALUE 区分平台矩阵3、定义一个枚举用于区分当前是处于什么相机 二、我们在DirectX平台下&#xff0c;看看效果1、正交相机下2、透视相机下3、最终代码 前言 在上一篇文章中&…

迁移Ubuntu报错问题

问题描述&#xff1a; 使用LxRunOffline-v3.5.0-mingw迁移Ubuntu至非系统盘时&#xff0c;出现如下报错 ‘Couldn’t set the case sensitive attribute of the directory “\?\C:\Users\xxx\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\Loc…

基于策略模式和简单工厂模式实现zip、tar、rar、7z四种压缩文件格式的解压

推荐语 这篇技术文章深入探讨了基于策略模式和简单工厂模式实现四种常见压缩文件格式的解压方法。通过阅读该文章&#xff0c;你将了解到如何利用这两种设计模式来实现灵活、可扩展的解压功能&#xff0c;同时适应不同的压缩文件格式。如果你对设计模式和文件处理感兴趣或刚好…

【ES】es介绍,使用spring-boot-starter-data-elasticsearch整合的ES来进行操作Es

文章目录 倒排索引&#xff08;Inverted Index&#xff09;和正排索引&#xff08;Forward Index&#xff09;es和MySQL对比IK分词器的总结mapping映射使用springboot整合的ES来进行操作Es1. 实体类中添加注解2. 编写Repository层3. 通过Repository进行增删改查 倒排索引&#…

学习Vue的key作用和原理

今天主要学习了列表渲染和key的作用和原理&#xff0c;先来说说列表渲染&#xff0c;顾名思义想要渲染列表最快的方式就是使用for循环&#xff0c;我们要学习的就是Vue中对标签实现for循环的语法&#xff0c;它和我们传统的js语法有些不同&#xff0c;它是先要有Vue实例中data的…

力扣热题100道-双指针篇

文章目录 双指针283.移动零11.盛最多水的容器15.三数之和42.接雨水 双指针 283.移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 …

C语言 volatile关键字

volatile关键字介绍 volatile 是一个关键字&#xff0c;用于修饰变量&#xff0c;表示该变量是易变的&#xff0c;即可能在任何时候被意外地改变。在多线程编程中&#xff0c;当多个线程同时访问同一个变量时&#xff0c;由于线程之间的交互和优化&#xff0c;可能会导致变量的…

亚信安慧AntDB数据库——通信运营商核心系统的全面演进

AntDB数据库源自通信运营商核心系统&#xff0c;经过15年的平稳运行和不断演进&#xff0c;成功跟随通信技术的升级步伐&#xff0c;逐步迈向5G时代&#xff0c;并且在这期间完成了8次大版本的迭代&#xff0c;为行业树立了技术领先的典范。其独特之处在于具备超融合架构&#…

vue本地缓存搜索记录(最多4条)

核心代码 //保存到搜索历史&#xff0c;最多存四个 item.name和item.code格式为&#xff1a;塞力斯000001var history uni.getStorageSync(history) || [];console.log("history", history)var index history.findIndex((items) > {return item.name items.nam…

通过nginx配置防御web漏洞

一、常见web漏洞 二、nginx防御策略&#xff1a; 要使用Nginx配置防御Web漏洞&#xff0c;可以采取以下措施&#xff1a; 禁用不必要的HTTP方法&#xff1a;Nginx默认启用了许多HTTP方法&#xff0c;如PUT、DELETE等。可以通过在Nginx配置中禁用不必要的HTTP方法来减少潜在的安…

Vue - 使用Element UI Upload / importExcelJs进行文件导入

1 情景一 需求背景&#xff1a;后端配合&#xff0c;点击"导入"按钮&#xff0c;弹出“导入”弹窗&#xff0c;将电脑本地Excel表格数据导入到页面中表格位置&#xff08;需要调用后端接口&#xff09;&#xff0c;而页面中表格通过后端接口获取最新数据。 实现思路…

学python一定要下载python吗,学python都需要什么软件

大家好&#xff0c;本文将围绕想学python都要下载什么软件展开说明&#xff0c;初学python需要安装什么软件是一个很多人都想弄明白的事情&#xff0c;想搞清楚学python一定要下载python吗需要先了解以下几个事情。 工欲善其事必先利其器。初学者在学Python的时候&#xff0c;往…