C++核心编程——P45-52继承

继承

继承是面向对象三大特性之一

有些类与类之间存在特殊的关系,例如下图中:

我们发现,定义这些类的时候,下级别的成员除了拥有上一级的共性,还有自己的特性。

这时候我们就可以考虑利用继承的技术,减少重复代码量。

1.继承的基本语法

例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同。

普通实现:

#include<iostream>
#include<string>
using namespace std;//普通实现页面//java页面
class Java
{
public:void header(){cout << "首页、登录注册、公开课、注册、..." << endl;}void footer(){cout << "帮助中心、交流合作、站内地图..." << endl;}void left(){cout << "java、python、c++..." << endl;}void contenet(){cout << "java学科视频" << endl;}
};
class Python
{
public:void header(){cout << "首页、登录注册" << endl;}void footer(){cout << "帮助中心、交流合作" << endl;}void left(){cout << "java、python、c++" << endl;}void contenet(){cout << "python学科视频" << endl;}
};
class Cpp
{
public:void header(){cout << "首页、登录注册" << endl;}void footer(){cout << "帮助中心、交流合作" << endl;}void left(){cout << "java、python、c++" << endl;}void contenet(){cout << "c++学科视频" << endl;}
};
void test()
{cout << "java页面" << endl;Java ja;ja.header();ja.footer();ja.left();ja.contenet();cout << endl;cout << "python页面" << endl;python py;py.header();py.footer();py.left();py.contenet();cout << endl;cout << "cpp页面" << endl;Cpp cpp;cpp.header();cpp.footer();cpp.left();cpp.contenet();
}
int main(void)
{test();system("pause");return 0;
}

继承实现:

#include<iostream>
#include<string>
using namespace std;//公共页面
class BasePage
{
public:void header(){cout << "首页、登录注册" << endl;}void footer(){cout << "帮助中心、交流合作" << endl;}void left(){cout << "java、python、c++" << endl;}
};//继承的好处:减少重复代码
//语法:class 子类 :继承方式父类子类也称为派生类
//父类也称为基类//普通实现页面
//java页面
class Java : public BasePage
{
public:void contenet(){cout << "java学科视频" << endl;}
};
//Python 页面
class Python : public BasePage
{
public:void contenet(){cout << "python学科视频" << endl;}
};
//Cpp 页面
class Cpp : public BasePage
{
public:	void contenet(){cout << "c++学科视频" << endl;}
};void test()
{cout << "java页面" << endl;Java ja;ja.header();ja.footer();ja.left();ja.contenet();cout << endl;cout << "python页面" << endl;python py;py.header();py.footer();py.left();py.contenet();cout << endl;cout << "cpp页面" << endl;Cpp cpp;cpp.header();cpp.footer();cpp.left();cpp.contenet();
}
int main(void)
{test();system("pause");return 0;
}

总结
继承的好处:减少重复代码

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

子类也称派生类

父类也称基类

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

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

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

2.继承方式

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

继承方式一共有三种

  • 公共继承
  • 保护继承
  • 私有继承

#include<iostream>
using namespace std;//公共继承
class Base1
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son1 :public Base1
{
public:void func(){m_A = 10;//父类中的公共权限成员,到了子类中依然是公共权限m_B = 20;//父类中的保护权限成员,到了子类中依然是保护权限//m_C = 10;父类中的隐私权限成员,子类访问不到}
};
void test01()
{Son1 son1;son1.m_A = 100;//son1.m_B = 100;保护权限的内容到了类外就无法访问了//私有的更不能访问
};//保护继承
class Base2
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son2 :protected Base2
{void func(){m_A = 100;//父类中公共权限的成员,因为是保护继承,到子类中变为保护权限m_B = 100;//父类中保护权限的成员,保护继承后到了子类还是保护权限。//m_C = 100;父类中的私有成员子类访问不到}
};
void test02()
{Son2 son2;//保护权限类外访问不到,所以在son2中m_A也访问不到了
}//私有继承
class Base3
{
public:int m_A;
protected:int m_B;
private:int m_C;
};
class Son3:private Base3
{void func(){m_A = 100;//父类中公共成员,私有继承后,到了子类变为私有成员m_B = 100;//父类中保护成员,私有继承后,到了子类变为私有成员//m_C = 100;父类的私有权限成员仍然访问不到}
};
void test03()
{Son3 son3;//私有成员类外访问不到
}
//验证Son3私有继承后成员是否变成了私有属性
class GrandSon3 :public Son3//孙子类继承儿子
{void func(){//访问不到父类的私有成员//到了Son3中m_A,m_B,m_C全是私有成员,子类无法访问}
};
int main(void)
{system("pause");return 0;
}

3.继承中的对象模型

问题:从父类继承过来的对象,哪些属于子类对象?

父类中所有的非静态成员属性都会被子类继承下去

父类中私有的成员属性是被编译器给隐藏了,因此访问不到,但是确实被继承下去了

#include<iostream>
using namespace std;
//继承中的对象模型
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 of son:" << sizeof(Son) << endl;//结果是16 = 12 + 4
}
int main(void)
{test01();system("pause");return 0;
}

利用VS的开发人员命令提示工具查看对象模型          

  1. 打开工具
  2. 跳转到你cpp文件所在的盘
  3. cd文件目录下
  4. 输入命令:cl /d1 reportSingleClassLayout类名 文件名

4.继承中构造和析构的顺序

子类继承父类后,当创建子类时,也会调用父类的构造函数。

问题:父类和子类的构造函数和析构顺序怎么样的呢?

先构造父类,再构造子类

先析构子类,再析构父类

创建子类对象的同时也会创建一个父类对象

#include<iostream>
using namespace std;
class Base
{
public:Base(){cout << "父类的构造函数" << endl;}~Base(){cout << "父类的析构函数" << endl;}
};
class Son:public Base 
{
public:Son(){cout << "子类的构造函数" << endl;}~Son(){cout << "子类的析构函数" << endl;}
};
void test01()
{Son son;
}
int main(void)
{test01();system("pause");return 0;
}

总结:继承中先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。

5.继承同名成员处理方式

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

  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需要加作用域
  • #include<iostream>
    using namespace std;
    class Base
    {
    public:Base(){m_A = 100;}void func(){cout << "父类同名成员函数调用" << endl;}void func(int a){cout << "父类同名重载成员函数调用" << endl;}int m_A;
    };
    class Son:public Base 
    {
    public:Son(){m_A = 200;}void func(){cout << "子类同名成员函数调用" << endl;}int m_A;
    };
    //同名成员属性处理方式
    void test01()
    {Son son;cout <<son.m_A<< endl;//如果要通过子类对象访问到父类中的同名成员,需要加作用域。//加作用域的意思是直接在指定作用域内进行名字查找,因为类名就是一个作用域cout <<son.Base::m_A<< endl;
    }
    //同名成员函数处理方式
    void test02()
    {Son son1; son1.func();//子son1.Base::func();//父//如果子类中出现和父类同名的成员函数//子类的同名成员会隐藏掉父类中所有同名成员函数//如果想要访问到父类中被隐藏的同名成员函数,需要加作用域son1.Base::func(10);
    }
    int main(void)
    {test02();system("pause");return 0;
    }

    总结

  • 子类对象可以直接访问到子类中同名成员
  • 子类对象加作用域可以访问到父类同名成员
  • 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类同名函数。

6.继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上是如何进行访问的呢?

静态成员和非静态成员出现同名,处理方式 一致。

  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需要加作用域
#include<iostream>
using namespace std;
class Base
{
public:static void func(){cout << "父类静态成员函数调用" << endl;}static void func(int a){cout << "父类静态成员重载函数调用" << endl;}static int m_A;
};
int Base::m_A = 100;
class Son :public Base
{
public:static void func(){cout << "子类静态成员函数调用" << endl;}static int m_A;
};
int Son::m_A = 200;
//同名静态成员
void test()
{//通过对象访问Son son1;cout << "通过对象访问" << endl;cout << son1.m_A << endl;cout << son1.Base::m_A << endl;//通过类名访问cout << "通过类名访问" << endl;cout << Son::m_A << endl;//第一个::代表通过类名方式访问,第二个::代表访问父类作用域下cout << Son::Base::m_A << endl;
}
//同名静态函数
void test01()
{//通过对象访问Son son2;cout << "通过对象访问" << endl;son2.func();son2.Base::func();  //通过类名访问cout << "通过类名访问" << endl;Son::func();Son::Base::func();//父类同名重载成员函数调用//子类出现和父类同名的静态成员函数,也会隐藏掉父类中所有同名成员函数(重载)//如果想访问父类中被隐藏的同名成员,需要加作用域Son::Base::func(100);
}
int main(void)
{test();cout << "我是分割线------" << endl;test01();system("pause");return 0;
}

总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象和类名)。

7.多继承语法

C++允许一个类继承多个类

语法:

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

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议使用多继承

 #include<iostream>
using namespace std;
//多继承语法
class Base1
{
public:Base1(){m_A = 100;}int m_A;
};
class Base2
{
public:Base2(){m_A = 200;}int m_A;
};
//子类需要继承base1和base2
class Son:public Base1,public Base2
{
public:Son(){m_C = 300;m_D = 400;}int m_C;int m_D;
};
void test01()
{	Son son1;cout << sizeof(son1) << endl;//16cout << "第一个父类的m_A:" << son1.Base1::m_A<<endl;cout << "第二个父类的m_A:" << son1.Base2::m_A<<endl;
}
int main(void)
{test01();system("pause");return 0;
}

总结:多继承中如果父类中出现了同名情况,子类使用时要加作用域。

8.菱形继承

菱形继承概念

两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承称为菱形继承,或者钻石继承。

典型的菱形继承案例

菱形继承问题

  1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
  2. 草泥马继承动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

vbptr——虚基类

继承了两个指针,两个指针通过偏移量找到了唯一的数据。

#include<iostream>
using namespace std;
class Animal
{
public:int m_Age;
};
//利用虚继承可以解决菱形继承问题
//在继承之前加上关键字virtual变为虚继承
// 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 = 18;st.Tuo::m_Age = 28;//当菱形继承,当两个父类拥有相同的数据,需要加作用域来区分cout << st.Sheep::m_Age << endl;cout << st.Tuo::m_Age << endl;cout << st.m_Age << endl;//这份数据我们知道,只有一份就可以了,菱形继承导致数据有两份,资源浪费
}
int main(void)
{test01();system("pause");return 0;
}

总结

  1. 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义。
  2. 利用虚继承可以解决菱形继承问题——virtual

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【一、虚拟机vmware安装】

安装虚拟机 下载 官方下载地址&#xff1a;https://www.vmware.com/cn.html 大概流程就是&#xff0c;最重要的事最后一步

【WSN】基于蚁群算法的WSN路由协议(最短路径)消耗节点能量研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【初阶数据结构】二叉树全面知识总结

二叉树详解 树的概念及其结构树的概念树的相关概念树的表示方法孩纸兄弟表示法双亲表示法&#xff08;并查集&#xff09; 树的实际应用 二叉树二叉树的概念二叉树的种类二叉树的性质二叉树的存储结构 二叉树顺序结构的实现堆的概念及结构堆向上、向下调整法堆的插入堆的删除堆…

YOLOv5如何训练自己的数据集

文章目录 前言1、数据标注说明2、定义自己模型文件3、训练模型4、参考文献 前言 本文主要介绍如何利用YOLOv5训练自己的数据集 1、数据标注说明 以生活垃圾数据集为例子 生活垃圾数据集&#xff08;YOLO版&#xff09;点击这里直接下载本文生活垃圾数据集 生活垃圾数据集组成&…

[Linux入门]---初识冯诺依曼体系

文章目录 1.背景知识2.冯诺依曼体系结构 1.背景知识 冯诺依曼&#xff08;John von Neumann&#xff09;&#xff0c;被称为“计算机之父”&#xff0c;确定了计算机的体系结构——即“冯诺依曼结构”&#xff1b;我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&am…

蓝桥杯每日一题2023.9.23

4961. 整数删除 - AcWing题库 题目描述 分析 注&#xff1a;如果要进行大量的删除操作可以使用链表 动态求最小值使用堆&#xff0c;每次从堆中取出最小值的下标然后在链表中删除 注意long long 代码解释&#xff1a; while(k --){auto t q.top();q.pop();res t.first;i…

经典题记录 字符串相加/相乘

1. LeetCode 415 字符串相加 代码一&#xff1a;代码简短&#xff0c;但需要借助额外的一个string来保存结果&#xff0c;更占用内存。 class Solution { public:string addStrings(string num1, string num2) {string ans"";int size1num1.size();int size2num2.si…

qt 6知识集

1.Use multi-arg instead [clazy-qstring-arg] 存在过个arg&#xff0c;连写形式如下&#xff1a; QString("%1 %2").arg(a).arg(b);QString("%1 %2").arg(a, b); // one less temporary heap allocation详见参考文献[1]。 2.widget运行后正常打开却看不…

基于Java的酒店管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Three.js后期处理简明教程

后期处理&#xff08;Post Processing&#xff09;通常是指对 2D 图像应用某种效果或滤镜。 在 THREE.js 中我们有一个包含一堆网格物体的场景。 我们将该场景渲染为 2D 图像。 通常&#xff0c;该图像会直接渲染到画布中并显示在浏览器中&#xff0c;但我们可以将其渲染到渲染…

双向链表的实现(增删查改)——最好理解的链表

双向链表的实现 一&#xff0c;双向链表的特点二&#xff0c;双向链表的结构三&#xff0c;双向链表的内容实现3.1创建node节点3.2初始化3.3打印3.4插入3.4.1尾插3.4.2头插3.4.3在pos位置上插入 3.5删除3.5.1尾删3.5.2头删3.5.3删除pos位置上的数据 四&#xff0c;调试技巧&…

html页面仿word文档样式(vue页面也适用)

目录 文章title&#xff1a; 标题&#xff1a; 正文&#xff1a; 完整代码&#xff1a; 页面效果&#xff1a; 文章title&#xff1a; <div><h3 style"display: flex;justify-content: center; align-items: center; color: #000;">实验室招新报名公…

【100天精通Python】Day69:Python可视化_实战:导航定位中预测轨迹和实际轨迹的3D动画,示例+代码

目录 1. 预测的3D轨迹和实际轨迹的动画图&#xff0c;同时动态更新 2 真值轨迹设置为静态的&#xff0c;预测轨迹不断更新 3 网格的三维坐标系有旋转运动&#xff0c;以此全方位展示预测轨迹和真值轨迹之间的空间关系 1. 预测的3D轨迹和实际轨迹的动画图&#xff0c;同时动态更…

Nginx 防止跨站脚本 Cross-Site Scripting (XSS)

1、修改 nginx 配置 在 nginx.conf 配置文件中&#xff0c;增加如下配置内容&#xff1a; add_header X-XSS-Protection "1; modeblock";X-XSS-Protection 的字段有三个可选配置值&#xff0c;说明如下&#xff1a; 0&#xff1a; 表示关闭浏览器的XSS防护机制&…

ad18学习笔记十一:显示和隐藏网络、铺铜

如何显示和隐藏网络&#xff1f; Altium Designer--如何快速查看PCB网络布线_ad原理图查看某一网络的走线_辉_0527的博客-CSDN博客 AD19(Altium Designer)如何显示和隐藏网络 如何显示和隐藏铺铜&#xff1f; Altium Designer 20在PCB中显示或隐藏每层铺铜-百度经验 AD打开与…

怎么将自己的Maven项目上传到Maven中央仓库/Maven阿里云云效仓库

前言 对于工作了多年的老程序员来说&#xff0c;往往会总结出一些比较好用的开发工具包。那么如果把这些好的工具插件共享出来供大家快速的使用呢&#xff0c;最好的方式就是将这些工具插件上传到Maven中央仓库/Maven阿里云云效仓库&#xff0c;这样&#xff0c;有需要用到这些…

八大排序(一)冒泡排序,选择排序,插入排序,希尔排序

一、冒泡排序 冒泡排序的原理是&#xff1a;从左到右&#xff0c;相邻元素进行比较。每次比较一轮&#xff0c;就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。 以从小到大排序为例&#xff0c;第一轮比较后&#xff0c;所有数中最大的那个数就会浮…

verilog学习笔记(1)module实例化

兜兜转转又回来学硬件了&#xff0c;哎&#xff0c;命啊&#xff01; 我的答案&#xff08;有bug&#xff09;&#xff1a; module top_module ( input a, input b, output out );wire w1;wire w2;wire w3;mod_a mod_a_inst1(.in1(w1),.in2(w2),.out(w3) );assign w1 a…

基于微信小程序的房屋租赁系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言运行环境说明用户微信小程序端的主要功能有&#xff1a;户主微信小程序端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文…

解决 Github port 443 : Timed out

解决方法 打开代理页面 打开 设置 --> 网络与Internet --> 查找代理 记录下当前系统代理的 IP 地址和端口号 如上图所示&#xff0c;地址与端口号为&#xff1a;127.0.0.1:7890 注意修改成自己的IP和端口号 git config --global http.proxy http://127.0.0.1:7890 gi…