「地表最强」C++核心编程(四)类和对象--继承

news2024/11/24 21:00:18

环境:
编译器:CLion2021.3;操作系统:macOS Ventura 13.0.1

文章目录

  • 一、继承的基本语法
  • 二、继承方式
    • 2.1 public继承
    • 2.2 protected继承
    • 2.3 private继承
    • 2.4 继承规则
  • 三、继承中的对象模型
  • 四、继承中的构造和析构顺序
  • 五、继承同名成员处理方式
  • 六、继承同名静态成员处理方式
  • 七、多继承语法
  • 八、菱形继承和虚继承

地表最强C++系列传送门:
「地表最强」C++核心编程(一)内存分区模型
「地表最强」C++核心编程(二)引用
「地表最强」C++核心编程(三)函数提高
「地表最强」C++核心编程(四)类和对象----封装
「地表最强」C++核心编程(五)文件操作——暂未更新

继承是C++的三大基本特性之一。

一、继承的基本语法

语法: class 子类 : 继承方式 父类

其中子类也称为派生类,父类也称为基类。派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。

//公共页面
class BasePage {
public:
    void header() {
        cout << "首页、公开课、登录、注册...(公共头部)" << endl;
    }

    void footer() {
        cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
    }

    void left() {
        cout << "Java,Python,C++...(公共分类列表)" << endl;
    }

};

//Java页面
class Java : public BasePage {
public:
    void content() {
        cout << "JAVA学科视频" << endl;
    }
};

//Python页面
class Python : public BasePage {
public:
    void content() {
        cout << "Python学科视频" << endl;
    }
};

//C++页面
class CPP : public BasePage {
public:
    void content() {
        cout << "C++学科视频" << endl;
    }
};

二、继承方式

继承方式一共有三种:公共继承、保护继承、私有继承。

2.1 public继承

class Base1 {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

//公共继承
class Son1 : public Base1 {
public:
    void func() {
        m_A = 10; //可访问 public权限
        m_B = 10; //可访问 protected权限
//        m_C; //不可访问
    }
};

void myClass() {
    Son1 s1;
    s1.m_A; //ok,其他类只能访问到公共权限
//    s1.m_B; //err,其他类只能访问到公共权限,m_B是protected,类外不可以访问
}

2.2 protected继承

//保护继承
class Base2 {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son2 : protected Base2 {
public:
    void func() {
        m_A; //可访问 protected权限
        m_B; //可访问 protected权限
        //m_C; //不可访问
    }
};

void myClass2() {
    Son2 s;
    //s.m_A; //不可访问,此时m_A变成了protected
}

2.3 private继承

//私有继承
class Base3 {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C;
};

class Son3 : private Base3 {
public:
    void func() {
        m_A; //可访问 private权限
        m_B; //可访问 private权限
        //m_C; //不可访问
    }
};

class GrandSon3 : public Son3 {
public:
    void func() {
        //Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到
//        m_A;//err
//        m_B;//err
//        m_C;//err
    }
};

2.4 继承规则

在这里插入图片描述

三、继承中的对象模型

父类中所有非静态成员属性都会被继承下去,私有成员属性只是访问不到,因为被编译器隐藏了,但是也被继承下去了。

class Base {
public:
    int m_A;
protected:
    int m_B;
private:
    int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};

//公共继承
class Son : public Base {
public:
    int m_D;
};

void test01() {
    cout << "sizeof Son = " << sizeof(Son) << endl;//16,说明private也被继承了
}

四、继承中的构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数,此时先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。

class Base {
public:
    Base() {
        cout << "Base构造函数!" << endl;
    }

    ~Base() {
        cout << "Base析构函数!" << endl;
    }
};

class Son : public Base {
public:
    Son() {
        cout << "Son构造函数!" << endl;
    }

    ~Son() {
        cout << "Son析构函数!" << endl;
    }
};

void test01() {
    //继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
    Son s;
}

五、继承同名成员处理方式

当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员,直接访问即可,也就是对象.属性
  • 访问父类同名成员,需要加作用域,也就是对象.父类::属性

需要注意的一点是:当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数,否则无法访问。强调一下,只要函数名相同,其他的不管一不一样,都会被隐藏

class Base {
public:
    int m_A;

public:
    Base() {
        m_A = 100;
    }

    void func() {
        cout << "Base - func()调用" << endl;
    }

    void func(int a) {
        cout << "Base - func(int a)调用" << endl;
    }
};


class Son : public Base {
public:
    int m_A;

public:
    Son() {
        m_A = 200;
    }

    //当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
    //如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
    void func() {
        cout << "Son - func()调用" << endl;
    }
};

void test01() {
    Son s;
    cout << "Son下的m_A = " << s.m_A << endl;
    cout << "Base下的m_A = " << s.Base::m_A << endl;//加个作用域就可以访问父类的

    s.func();
    s.Base::func();
    //子类中的同名成员函数会隐藏掉父类有所有同名成员函数,包括重载的,要想访问父类中被隐藏的同名成员函数的需要加作用域
//    s.func(10);//err,父类中的有参函数被隐藏,而子类又没有有参函数,需要加作用域
    s.Base::func(10);//ok

}

六、继承同名静态成员处理方式

静态成员和非静态成员出现同名,处理方式一致,只不过有两种访问的方式:通过对象通过类名。比非静态多了一个通过类名访问,这是静态成员本身的特性所导致的。
关于静态成员,可以参考「地表最强」C++核心编程(四)类和对象—对象初始化和清理第八点。

class Base {
public:
    static int m_A;

    static void func() {
        cout << "Base - static void func()" << endl;
    }

    static void func(int a) {
        cout << "Base - static void func(int a)" << endl;
    }
};
int Base::m_A = 100;

class Son : public Base {
public:
    static int m_A;

    static void func() {
        cout << "Son - static void func()" << endl;
    }
};
int Son::m_A = 200;

//同名成员属性
void test01() {
    //通过对象访问
    cout << "通过对象访问: " << endl;
    Son s;
    cout << "Son  下 m_A = " << s.m_A << endl;
    cout << "Base 下 m_A = " << s.Base::m_A << endl;

    //通过类名访问
    cout << "通过类名访问: " << endl;
    cout << "Son  下 m_A = " << Son::m_A << endl;
    
    //第一个::代表通过类名访问,第二个::代表访问父类作用域下
    cout << "Base 下 m_A = " << Son::Base::m_A << endl;//这样通过子类
//    cout << "Base 下 m_A = " << Base::m_A << endl;//这是直接通过父类访问,也是对的
}

//同名成员函数
void test02() {
    //通过对象访问
    cout << "通过对象访问: " << endl;
    Son s;
    s.func();
    s.Base::func();

	//通过类名访问
    cout << "通过类名访问: " << endl;
    Son::func();
    Son::Base::func();
    //出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
    Son::Base::func(100);
//    Son::func(100);//err,父类中的同名函数被隐藏了
}

七、多继承语法

C++允许一个类继承多个类,就是多继承。但多继承可能会引发从父类中继承多个同名成员,需要加作用域区分。C++实际开发中不建议用多继承。

语法: class 子类 : 继承方式 父类1 , 继承方式 父类2…

class Base1 {
public:
    int m_A;

public:
    Base1() {
        m_A = 100;
    }
};

class Base2 {
public:
    int m_A;

public:
    Base2() {
        m_A = 200;  //若是m_B则不会出问题,但是改为mA就会出现不明确
    }
};

//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1 {
public:
    int m_C;
    int m_D;

public:
    Son() {
        m_C = 300;
        m_D = 400;
    }
};


//多继承容易产生成员同名的情况,这是就要通过类名作用域来区分调用的是哪一个基类的成员
void test01() {
    Son s;
    cout << "sizeof Son = " << sizeof(s) << endl;
    cout << s.Base1::m_A << endl;
    cout << s.Base2::m_A << endl;
}

八、菱形继承和虚继承

两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承,或者钻石继承。
菱形继承的问题:
1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
2.草泥马继承自动物的数据继承了两份,但这个据我们只需要一份,这导致资源浪费而且毫无意义。
通过虚继承就可以解决菱形继承带来的问题。

语法: class 子类 : virtual 继承方式 父类

virtual关键字使得继承方式变成了虚继承,此时两个子类从同一父类那里继承来的是虚基类指针vbptr(virtual vase pointer),这个指针指向了各自的vbtable(虚基类表),这个表中记录了一个偏移量,通过这个偏移量就可以找到需要的数据,这个数据只有一份。
在这里插入图片描述

class Animal {
public:
    int m_Age;
};

//虚继承,此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {
};
class Tuo : virtual public Animal {
};

//羊驼类
class SheepTuo : public Sheep, public Tuo {
};

void test01() {
    SheepTuo st;
    //相同数据加作用域区分即可
    st.Sheep::m_Age = 100;
    cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;//100
    
    st.Tuo::m_Age = 200;//由于虚继承数据只有一份,操作这个会更改之前的赋值

    cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;//200,确实更改了
    cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;//200
    cout << "st.m_Age = " << st.m_Age << endl;//由于virtual,这种访问方式可以了,否则是错误的
}

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

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

相关文章

PyQt5利用Qt designer(QT设计师)使用tab widget和stacked widget实现多页面切换

PyQt5 Qt designer QT设计师 使用tab widget和stacked widget实现多页面切换一、使用Qt designer(QT设计师)进行多页面切换ui设计二、实现tab widget多页面切换三、实现stacked widget多页面切换四、生成代码五、运行效果一、使用Qt designer(QT设计师)进行多页面切换ui设计 本…

Go 实现线性查找算法和二分查找算法

耐心和持久胜过激烈和狂热。 哈喽大家好&#xff0c;我是陈明勇&#xff0c;今天分享的内容使用 Go 实现线性查找算法和二分查找算法。如果本文对你有帮助&#xff0c;不妨点个赞&#xff0c;如果你是 Go 语言初学者&#xff0c;不妨点个关注&#xff0c;一起成长一起进步&…

雪花算法原理

SnowFlake算法生成id的结果是一个64bit大小的整数&#xff0c;它的结构如下图&#xff1a;1bit&#xff0c;不用&#xff0c;因为二进制中最高位是符号位&#xff0c;1表示负数&#xff0c;0表示正数。生成的id一般都是用整数&#xff0c;所以最高位固定为0。41bit时间戳&#…

热门技术中的应用:云计算中的网络-第27讲-云中的网络QoS:邻居疯狂下电影,我该怎么办?

在小区里面,是不是经常有住户不自觉就霸占公共通道,如果你找他理论,他的话就像一个相声《楼道曲》说的一样:“公用公用,你用我用,大家都用,我为什么不能用?”。 除此之外,你租房子的时候,有没有碰到这样的情况:本来合租共享WiFi,一个人狂下小电影,从而你网都上不…

编程15年40岁程序员的我终于在压力下被迫转行了

本人今年40岁多了&#xff0c;中山大学计算机小硕&#xff0c;已经从事it工作15年多&#xff0c;最后一次工作是2017年&#xff0c;创业&#xff0c;互联网教育方向&#xff0c;2020年失败关闭公司。 创业失败后&#xff0c;在家沉淀了几个月&#xff0c;然后决定再次找工作。…

如何在UnrealEngine虚幻引擎中进行版本管理

项目团队中的分工协作必不可少&#xff0c;在UE项目中进行版本控制非常必要。UE支持使用Perforce和SVN进行版本管理&#xff0c;此处选用自己比较熟悉的SVN。 1.使用SVN进行源码管理 通过编辑器偏好设置窗口&#xff08;编辑&#xff08;Edit&#xff09;> 编辑器偏好设置&…

9. Spring注解开发

1. 注解开发定义Bean对象 目的&#xff1a;xml配置Bean对象有些繁琐&#xff0c;使用注解简化Bean对象的定义 1.1 在applicationContext.xml中开启Spring注解包扫描 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.spr…

33 CPP类多态-如何析构派生类

33 CPP类多态-如何析构派生类 派生类的析构函数在执行完后&#xff0c;会自动执行基类的析构函数&#xff0c;这是C编译器强制的规定。 这时候基类的内存模型&#xff1a;AA表示的就是Person类 将基类的析构函数设置为虚函数后。 基类的虚函数表中多了一个函数&#xff0c;但是…

性能测试(二)—— 常用测试工具、JMeter环境搭建、JMeter功能概述

目录 一、常用性能测试工具 1. 主流性能测试工具 1.1 LoadRunner 1.2 JMeter 1.3 LoadRunner 与 JMeter对比 二、JMeter环境搭建 1. 安装JDK 1.1 JDK下载 1.2 JDK配置环境变量 2. 安装JMeter 2.1 下载 2.2 安装 2.3 Jmeter环境配置 2.4 启动验证 三、JMeter功能…

[附源码]计算机毕业设计Python的中点游戏分享网站(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

反应式编程框架设计:如何使得程序调用不阻塞等待

前言&#xff1a; 程序在高并发的情况下&#xff0c;程序容易崩溃。主要的原因是&#xff1a;在高并发的情况下&#xff0c;有大量用户请求需要程序计算处理&#xff0c;而目前的处理方式是&#xff0c;为每个用户请求分配一个线程&#xff0c;当程序内部因为访问数据库等原因…

软件测试:Java VS Python,刚开始应该选择哪门语言进行入门呢?

前言 当你学完软件测试基本理论&#xff0c;掌握业务测试流程&#xff0c;功能测试可以搞定&#xff0c;数据库和linux玩得也很溜时&#xff0c;接下来想进一步进阶&#xff0c;那么学习一门编程语言必不可少。 同时&#xff0c;学习一门编程语言也是你成为自动化测试工程师乃…

如何正确使用JMeter性能测试?紧扣面试实际要求

前段时间专门挑了一段时间在准备面试。经过两次面试后&#xff0c;有一些比较深刻的认识。对于企业要求来说&#xff0c;除了对专业理论知识考究之外&#xff0c;对测试工具这块也是看重的。 一、使用JMeter测试快速入门 1、线程组是什么 进程&#xff1a; 一个正在执行的程序…

图解设计模式: 有趣的工厂模式

工厂模式 Factory Method 在工厂模式中 父子类的关系就像是生产工厂中模具一样, 由父类负责指定实例生成的方式 子类来决定生成具体的类. 具体的处理全部交给子类负责&#xff0c;目的就是为了将生产实例的框架和负责实例生产类解耦 示例程序 从下面这段示例来看看工厂模式到…

Codeforces Round #839 (Div. 3)

A. AB? 题意&#xff1a; t组测试每组给出以ab的形式给出算式&#xff0c;求ab的值。&#xff08;a,b都是0~9&#xff09; 代码&#xff1a; tint(input()) for i in range(t):sinput()print(eval(s)) B. Matrix Rotation 题意&#xff1a; t组测试每组给一个2*2的矩阵&…

NoSQL数据库原理与应用综合项目——Redis篇

NoSQL数据库原理与应用综合项目——Redis篇 文章目录NoSQL数据库原理与应用综合项目——Redis篇0、 写在前面1、本地数据或HDFS数据导入到Redis2、Redis数据库表操作2.1 Java API 连接Redis2.2 查询数据2.3 插入数据2.4 修改数据2.5 删除数据3、Windows远程连接Redis(Linux)4、…

谁说女生不可以学编程?维密超模放弃年薪千万,一心只当程序媛

说到IT行业&#xff0c;大家第一反应应该是程序员 谁说女生不可以学编程&#xff1f;维密超模放弃年薪千万&#xff0c;一心只当程序媛 感觉一说起IT工作者 大家都会想到一个男性的形象 但是其实 有一批脸美、胸大、腰细、腿长、还都热爱编程的妹纸正在加入你们的行列&#…

(附源码)Springboot宠物领养系统 毕业设计 241104

Springboot宠物领养系统 摘 要 如今&#xff0c;随着人们生活水平不断提高&#xff0c;人们的生活在物质满足的基础上&#xff0c;更多的人将生活的重点放在追求精神享受的过程中。于此同时&#xff0c;Internet铺天盖地的普及&#xff0c;使得这样的人纷纷通过Internet的方式去…

springboot 3.0 工程建立

springboot 3.0 工程建立 脚手架搭建 进入spring官网提供的https://start.spring.io/进行脚手架搭建。 选择 Maven进行包管理&#xff0c;语言选择JAVA&#xff0c;Spring Boot 版本选择3.0.0&#xff0c;JDK 版本选择17。并在右侧选择自己希望的依赖。结果如下图&#xff1…

基于SpringMVC+Hibernate+Layui城市智能消防决策平台设计

开发软件&#xff1a;Eclipse,可以用idea,mysql数据库 开发技术&#xff1a;SpringMVC,Spring,Hibernate,jquery,layui 本系统的功能主要分为两个角色&#xff0c;其中用户的功能有&#xff1a;登陆注册&#xff0c;查看火灾案例&#xff0c;火警报警&#xff0c;查看自己的报警…