学习【菜鸟教程】【C++ 继承】

news2024/11/15 1:47:33

链接

1. 教程原文

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。

当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。

继承代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。
在这里插入图片描述
代码如下:

// 基类
class Animal {
    // eat() 函数
    // sleep() 函数
};

//派生类
class Dog : public Animal {
    // bark() 函数
};

1.1 基类 & 派生类

一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:

class derived-class: access-specifier base-class

其中,访问修饰符 access-specifier 是 publicprotectedprivate 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。

假设有一个基类 ShapeRectangle 是它的派生类,如下所示:

#include <iostream> 
using namespace std;
 
// 基类
class Shape{
   public:
      void setWidth(int w){
         width = w;
      }
      void setHeight(int h){
         height = h;
      }
   protected:
      int width;
      int height;
};
 
// 派生类
class Rectangle: public Shape{
   public:
      int getArea(){ 
         return (width * height); 
      }
};
 
int main(void){
   Rectangle Rect;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
 
   // 输出对象的面积
   cout << "Total area: " << Rect.getArea() << endl;
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35

1.2 访问控制和继承

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。

我们可以根据访问权限总结出不同的访问类型,如下所示:

访问publicprotectedprivate
同一个类yesyesyes
派生类yesyesno
外部的类yesnono

一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。

1.3 继承类型

当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。

我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:

  • 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
  • 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
  • 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

1.4 多继承

多继承即一个子类可以有多个父类,它继承了多个父类的特性。

C++ 类可以从多个类继承成员,语法如下:

class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,{
<派生类类体>
};

其中,访问修饰符继承方式是 public、protected 或 private 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。现在让我们一起看看下面的实例:

#include <iostream> 
using namespace std;
 
// 基类 Shape
class Shape{
   public:
      void setWidth(int w){
         width = w;
      }
      void setHeight(int h){
         height = h;
      }
   protected:
      int width;
      int height;
};
 
// 基类 PaintCost
class PaintCost{
   public:
      int getCost(int area){
         return area * 70;
      }
};
 
// 派生类
class Rectangle: public Shape, public PaintCost{
   public:
      int getArea(){ 
         return (width * height); 
      }
};
 
int main(void){
   Rectangle Rect;
   int area;
 
   Rect.setWidth(5);
   Rect.setHeight(7);
 
   area = Rect.getArea();
   
   // 输出对象的面积
   cout << "Total area: " << Rect.getArea() << endl;
 
   // 输出总花费
   cout << "Total paint cost: $" << Rect.getCost(area) << endl;
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Total area: 35
Total paint cost: $2450

2. 评论笔记

2.1 其一

另外多继承(环状继承),A->D, B->D, C->(A,B),例如:

class D{......};
class B: public D{......};
class A: public D{......};
class C: public B, public A{.....};

这个继承会使D创建两个对象,要解决上面问题就要用虚拟继承格式

格式:class 类名: virtual 继承方式 父类名

class D{......};
class B: virtual public D{......};
class A: virtual public D{......};
class C: public B, public A{.....};

虚继承–(在创建对象的时候会创建一个虚表)在创建父类对象的时候

A:virtual public D
B:virtual public D

实例:

#include <iostream>
using namespace std;
//基类

class D{
public:
    D(){cout<<"D()"<<endl;}
    ~D(){cout<<"~D()"<<endl;}
protected:
    int d;
};

class B:virtual public D{
public:
    B(){cout<<"B()"<<endl;}
    ~B(){cout<<"~B()"<<endl;}
protected:
    int b;
};

class A:virtual public D{
public:
    A(){cout<<"A()"<<endl;}
    ~A(){cout<<"~A()"<<endl;}
protected:
    int a;
};

class C:public B, public A
{
public:
    C(){cout<<"C()"<<endl;}
    ~C(){cout<<"~C()"<<endl;}
protected:
    int c;
};

int main(){
    cout << "Hello World!" << endl;
    C c;   //D, B, A ,C
    cout<<sizeof(c)<<endl;
    return 0;
}
  • 1、与类同名的函数是构造函数。
  • 2、~ 类名的是类的析构函数。

2.2 其二

为什么子类的构造函数中会出现在初始化列表中呢?原因在于子类能够从基类继承的内容限制上。

我们知道,一个派生类继承了所有的基类方法,但下列情况除外:

  • 基类的构造函数、析构函数和拷贝构造函数。
  • 基类的重载运算符。
  • 基类的友元函数。

因此,我们不能够在子类的成员函数体中调用基类的构造函数来为成员变量进行初始化。例如这样子是不可以的

#include <iostream> 
using namespace std;
 
// 基类
class Shape{
   public:
      Shape(int w,int h){
        width=w;
        height=h;
      }
   protected:
      int width;
      int height;
};
 
// 派生类
class Rectangle: public Shape{
   public:
      Rectangle(int a,int b){
        Shape(a,b);
      }     
};

但我们可以把基类的构造函数放在子类构造函数的初始化列表上,以此实现调用基类的构造函数来为子类从基类继承的成员变量初始化。

#include <iostream> 
using namespace std;
 
// 基类
class Shape{
   public:
      Shape(int w,int h){
        width=w;
        height=h;
      }
   protected:
      int width;
      int height;
};
 
// 派生类
class Rectangle: public Shape{
   public:
      Rectangle(int a,int b):Shape(a,b)
      {        
      }
};

2.3 其三

关于构造函数初始化列表的执行顺序进行补充:

#include<iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout << "call A()" << endl;
    }
};

class B :A
{
public:
    B(int val) : A(), value(val)
    {
        val = 0;    // 重新赋值
        cout << "call B()" << endl;
        cout << val << endl;    
    }

private:
    int value;
};

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

/*
* 结果如下 
* call A()
* call B()
* 0
* 说明放在初始化列表的部分在构造函数之前执行
*/

2.4 其四

派生类在继承基类的成员变量时,会单独开辟一块内存保存基类的成员变量,因此派生类自己的成员变量即使和基类的成员变量重名,但是也不会引起冲突。如下代码:

#include <iostream>
using namespace std;

//基类
class A{
public:
   A(){n = 0;};
   ~A(){};
   int getA(){ return n;};
   void setA(int t){ n = t;};

private:
   int n;
};

//派生类
class B :public A{
public:
   B(){ n = 0;};
   ~B(){};
   int getB(){ return n;};
   void setB(int t){ n = t;};

private:
  int n;
};

int main(int argc, char * argv[]){
   B b;
   b.setA(10); //设置基类的成员变量n

   cout<<"A::n "<<b.getA()<<endl;                                                                       
   cout<<"B::n "<<b.getB()<<endl;                                                                                                                                                         
   b.setB(9);  //设置派生类的成员变量n                                                                                                                     
   cout<<"A::n "<<b.getA()<<endl;                                                                       
   cout<<"B::n "<<b.getB()<<endl;                                                                                                                            
   return 0;                                                                                 
}

结果如下:

A::n 10
B::n 0
A::n 10
B::n 9

2.5 其五

构造函数调用顺序:基类 > 成员类 > 派生类;

多继承派生类: 基类构造顺序 依照 基类继承顺序调用

类成员:依照 类成员对象 定义顺序 调用成员类构造函数

#include <iostream>
using namespace std;

class Shape {   // 基类 Shape
public:
    Shape() {
        cout << "Shape" << endl;
    }
    ~Shape() {
        cout << "~Shape" << endl;
    }
};

class PaintCost {   // 基类 PaintCost
public:
    PaintCost() {
        cout << "PaintCost" << endl;
    }
    ~PaintCost() {
        cout << "~PaintCost" << endl;
    }
};

// 派生类
class Rectangle : public Shape, public PaintCost  //基类构造顺序 依照 继承顺序
{
public:
    Rectangle() :b(), a(), Shape(), PaintCost(){
        cout << "Rectangle" << endl;
    }
    ~Rectangle() {
        cout << "~Rectangle" << endl;
    }
    PaintCost b;        // 类成员变量构造顺序 依照 变量定义顺序
    Shape a; 
};

int main(void)
{
    Rectangle Rect;
    return 0;
}

结果:

Shape
PaintCost
PaintCost
Shape
Rectangle
~Rectangle
~Shape
~PaintCost
~PaintCost
~Shape

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

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

相关文章

精通postman教程(一)下载及安装详解

作为一名测试工程师&#xff0c;那么Postman绝对是大伙必备的工具之一。 在这个系列教程中&#xff0c;我将为大伙详细讲解如何使用Postman进行API测试。 今天我将先为大伙介绍Postman的下载安装方法&#xff0c;让你们快速上手这款工具。 一、下载 下载地址&#xff1a;Do…

基于Java学院党员管理系统设计与实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

Java学习笔记(视频:韩顺平老师)4.0

如果你喜欢这篇文章的话&#xff0c;请给作者点赞哟&#xff0c;你的支持是我不断前进的动力。 因为作者能力水平有限&#xff0c;欢迎各位大佬指导。 目录 如果你喜欢这篇文章的话&#xff0c;请给作者点赞哟&#xff0c;你的支持是我不断前进的动力。 控制结构 顺序 分…

【面试系列】2023金三银四面经

&#x1f431; 个人主页&#xff1a;不叫猫先生&#xff0c;公众号&#xff1a;前端舵手 &#x1f64b;‍♂️ 作者简介&#xff1a;2022年度博客之星前端领域TOP 2&#xff0c;前端领域优质作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步…

深入浅出RTA广告投放

一、RTA诞生背景 广告主在媒体投放广告时&#xff0c;往往需要将数据回传给DSP或媒体平台&#xff0c;供他们进行针对性优化。但是随着很多公司对数据隐私性的重视和保护&#xff0c;导致广告主不能或者不愿意将数据回传给DSP或媒体平台&#xff0c;但这样做又必然导致投放模型…

Lightroom Classic 2023(版本 12.3)主要新增功能

macw发布了Adobe Lightroom Classic 2023(版本 12.3)软件&#xff0c;该版本新增了哪些功能呢&#xff1f;随着小编一起了解一下吧&#xff01; 主要新增功能概述 轻松消除图像中的杂色 借助 AI 支持的降噪功能&#xff0c;可以轻松、有效地消除 RAW 图像中的杂色&#xff0c…

抽象工厂模式(六)

过气的&#xff0c;终究是过气了 上一章简单介绍了工厂方法模式(五), 如果没有看过,请观看上一章 一. 抽象工厂模式 引用 菜鸟教程里面的单例模式介绍: https://www.runoob.com/design-pattern/abstract-factory-pattern.html 抽象工厂模式&#xff08;Abstract Factory Pat…

记Gitlab备份与设置自动备份

今天给Gitlab做了一个备份&#xff0c;并且设置了每天自动备份&#xff0c;记录一下。 一、导出全部项目 由于Gitlab Web页面并没有自动备份的相关设置&#xff0c;只有各个项目有一个“导出项目”功能。为了保证安全&#xff0c;先把所有项目全部使用“导出项目”功能导出一…

Redis实战案例1-短信登录

Redis的共享session应用 1. 项目的相关工作 导入sql文件 找到对应的sql文件即可 基本表的信息 基本架构 导入对应的项目文件&#xff0c;启动相关的service服务; 在nginx-1.18.0目录下启动命令行start nginx.exe&#xff1b; 2. 基于session实现登录的流程 这里利用到Javaweb中…

脚踩Midjourney、Stable Diffusion,谷歌StyleDrop真要杀疯了!

脚踩Midjourney、Stable Diffusion&#xff0c;谷歌StyleDrop真要杀疯了 导语StyleDrop组件MuseMuse架构Muse图像生成实现流程 适配器微调Adapter TuningMuse中的Adapter Tuning 反馈迭代训练 最近&#xff0c;谷歌发布了一款引人瞩目的AI绘画工具——StyleDrop。这个工具通过学…

ZYNQ - 以太网远程更新贴片SD卡应用程序【SD NAND应用】

写在前面 对于ZYNQ系列的板卡固化&#xff0c;可以通过JTAG接口&#xff0c;使用SDK固化到FLASH中&#xff0c;或者可将SD卡取出将SD卡中保存的固化工程进行修改&#xff0c;但在很多情况下&#xff0c;离线更新会很不方便&#xff0c;本文借鉴网上常见的远程更新QSPI FLASH的…

解析Java异常机制:捕获编程中的错误,保障代码稳定性

工作中&#xff0c;程序遇到的情况不可能完美。比如&#xff1a;程序要打开某个文件&#xff0c;这个文件可能不存在或者文件格式不对&#xff1b;程序在运行着&#xff0c;但是内存或硬盘可能满了等等。 软件程序在运行过程中&#xff0c;非常可能遇到刚刚提到的这些问题&…

java OpenCSV自定义列标题和列位置

背景:最近使用csv进行数据导出&#xff0c;提高导出性能 问题&#xff1a;CsvBindByName和CsvBindByPosition不能同时用&#xff0c;要么是没标题要么是不是指定的排序规则 实现思路&#xff1a; 自定义排序规则&#xff0c;HeaderNameBaseMappingStrategy的writeOrder 属性可…

C++基于jrtp实现rtp发送与接收代码实现(附源码)

C++常用功能源码系列 文章目录 C++常用功能源码系列前言一、jrtp是什么二、rtp sender源码三、rtp receive源码总结前言 本文是C/C++常用功能代码封装专栏的导航贴。部分来源于实战项目中的部分功能提炼,希望能够达到你在自己的项目中拿来就用的效果,这样更好的服务于工作实…

Mininet拓扑构建与命令使用

实验目的&#xff1a; 1、通过命令模拟创建SDN网络。 2、深入了解Mininet的命令使用。 3、学会使用网络构建启动参数、内部交互命令及外部运行参数。 实验环境&#xff1a; 设备名称软件环境硬件环境主机Mininet_2.2.0_desktop_cv1.1CPU&#xff1a;1核 内存&#xff1a;2G 磁…

【MySQL数据库】MySQL数据库管理

MySQL数据库管理 一、数据库简述1.1简介1.2数据库基本概念1.3数据库的发展史1.3主流的数据库介绍1.4数据库的类型1.4.1关系型数据库特点1.4.2非关系型数据库介绍 1.5SQL语言分类 二、数据库基础应用2.1查看数据库2.2创建新的库、表2.3删除库、表2.4管理表中的数据记录2.4.1插入…

Java新技术和趋势:如何应对Java生态的变化和发展趋势

章节一&#xff1a;引言 Java是一门广泛使用的编程语言&#xff0c;具有强大的生态系统和持续的发展。随着时间的推移&#xff0c;Java生态系统不断演进和改变&#xff0c;出现了许多新技术和趋势。在本文中&#xff0c;我们将探讨一些最新的Java技术和趋势&#xff0c;并分享…

路径规划算法:基于适应度相关优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于适应度相关优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于适应度相关优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能…

路径规划算法:基于社会群体优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于社会群体优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于社会群体优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化…

【Linux】Nginx+Tomcat负载均衡、动静分离、多实例部署

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 NginxTomcat负载均衡、动静分离、多实例部署 一、Tomcat 多实例部署1.安装好 jdk 及 tomcat2.配置 tomcat 环境变量3.修改 tomcat2 中的 server.xml 文件4.修改各 tomcat 文件…