C++面向对象--------继承篇

news2024/11/30 0:39:15

目录

一.继承(重点)

1.1 概念

1.2 构造函数

1.2.1 派生类与基类的构造函数关系

1.2.2 解决方案

1.2.2.1 补充基类的无参构造函数

1.2.2.2 手动在派生类中调用基类构造函数

1.2.2.2.1 透传构造

1.2.2.2.2 委托构造

1.2.2.2.3 继承构造

1.3 对象的创建与销毁流程(掌握)

1.4 多重继承(掌握)

1.4.1 概念

1.4.2 可能出现的问题

1.4.2.1 问题1-重名问题(掌握)

1.4.2.2 问题2-菱形继承(熟悉)

二.权限

2.1 公有继承

2.2 保护继承

2.3 私有继承


一.继承(重点)

1.1 概念

继承是面向对象的三大特性之一,主要体现了代码复用的思想。

继承就是在一个已存在的类的基础上,建立一个新的类。并拥有其特性。

● 已存在的类被称为“基类”或者“父类”

● 新建立的类被称为“派生类”或者“子类”

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    void set_name(string name)
    {
        this->name = name;
    }

    string get_name()
    {
        return name;
    }

    void work()
    {
        cout << "我的工作是厨师,我负责炒菜" << endl;
    }
};

// 派生类
class Son:public Father // public:继承权限,基类的所有权限,在派生类中保持不变
{

};

int main()
{
    Son son;
    cout << son.get_name() << endl;

    son.work();
    return 0;
}
上面的代码,Son类的功能几乎与Father类重叠。在实际的开发使用过程中,派生类会做出一些与基类的差异化。

● 修改继承来的基类内容

○ 属性:1、公有属性可以直接改。更改后基类的属性也会改变,因为改的是同一份变量。私有属性,需要使用基类的公有函数进行更改。

○ 函数:函数隐藏,通过派生类实现一个同名同参数的函数,来隐藏基类的函数。

● 新增派生类的内容

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    void set_name(string name)
    {
        this->name = name;
    }

    string get_name()
    {
        return name;
    }

    void work()
    {
        cout << "我的工作是厨师,我负责炒菜" << endl;
    }
};

// 派生类
class Son:public Father // public:继承权限,基类的所有权限,在派生类中保持不变
{
public:
    void init()
    {
//        name = "王"; // 错误 基类的私有属性,派生类无法直接访问
        set_name("王");
    }

    void work()
    {
        cout << "我是程序猿,我在打代码" << endl;
    }

    void game()
    {
        cout << "我不光干活,我还打游戏,原神启动。杂交版启动" << endl;
    }
};


int main()
{
    Son son;
    son.init();
    cout << son.get_name() << endl; // 王

    son.work(); // 我是程序猿,我在打代码
    son.game(); // 我不光干活,我还打游戏,原神启动。杂交版启动

    son.Father::work(); // 调用基类被隐藏的成员函数

    return 0;
}

基类与派生类是相对的,一个类可能存在又是基类又是派生类的情况,取决于那两个类进行比较。

1.2 构造函数

1.2.1 派生类与基类的构造函数关系

构造函数与析构函数不能被继承。

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    // 有参构造函数
    Father(string name):name(name){}

    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:

};

int main()
{
//    Son s; // 找不到基类的无参构造函数
    Son s("张"); // 没有匹配的构造函数

    return 0;
}

1.2.2 解决方案

1.2.2.1 补充基类的无参构造函数
#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    // 补充基类无参构造函数
    Father()
    {
        cout << "构造函数被调用了" << endl;
    }
    // 有参构造函数
    Father(string name):name(name){}

    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    // 编译器自动添加的构造函数
    Son():Father()
    {
        cout << "派生类构造函数被调用了" << endl;
    }

};

int main()
{
    Son s; // 找不到基类的无参构造函数
//    Son s("张"); // 没有匹配的构造函数

    return 0;
}
1.2.2.2 手动在派生类中调用基类构造函数
1.2.2.2.1 透传构造

在派生类的构造函数中,调用基类的构造函数,实际上编译器自动添加的构造函数,调用基类无参构造函数时,采用的就是这种方式。

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    // 透传构造
    Son():Father("张"){}

    // 派生类有参构造函数,调用基类有参构造函数
    Son(string fn):Father(fn){}
};

int main()
{
    Son s;
    cout << s.get_name() << endl;

    Son s1("王");
    cout << s1.get_name() << endl;

    return 0;
}

1.2.2.2.2 委托构造

一个类的构造函数可以调用这个类的另一个构造函数,但是要避免循环委托。

委托构造的性能要低于透传构造,但是代码的维护性“更好”。因为通常一个类中构造函数都会委托给能力最强(参数最多)的构造函数,在代码重构时,只需要更改这个能力最强的构造函数即可。

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    // 有参构造函数
    
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    // 委托构造
    Son():Son("张"){}

    // 派生类有参构造函数,调用基类有参构造函数
    Son(string fn):Father(fn){}
};

int main()
{
    Son s;
    cout << s.get_name() << endl;

//    Son s1("王");
//    cout << s1.get_name() << endl;

    return 0;
}
1.2.2.2.3 继承构造

C++11 新增的写法,只需要一句话,就可以自动给派生类添加n(n为基类构造函数的个数(不包含默认无参构造函数))个构造函数。并且每个派生类的构造函数的格式都与基类相同,每个派生类的构造函数都通过透传构造调用对应格式的基类构造函数。

#include <iostream>

using namespace std;

// 基类
class Father
{
private:
    string name = "孙";
public:
    Father():Father("张"){}
    // 有参构造函数
    Father(string name):name(name){}
    string get_name()
    {
        return name;
    }
};

// 派生类
class Son:public Father
{
public:
    using Father::Father;
//    // 委托构造
//    Son():Son(){}

//    // 派生类有参构造函数,调用基类有参构造函数
   // Son(string fn):Father(fn){}
};

int main()
{
    Son s;
    cout << s.get_name() << endl;

//    Son s1("王");
//    cout << s1.get_name() << endl;

    return 0;
}

1.3 对象的创建与销毁流程(掌握)

在继承中,构造函数与析构函数的调用顺序

#include <iostream>

using namespace std;
class Value
{
private:
    string str;
public:
    Value(string str):str(str)
    {
        cout << str << "构造函数" << endl;
    }

    ~Value()
    {
        cout << str <<"析构函数" << endl;
    }
};


class Father
{
public:
    static Value s_value;
    Value val = Value("Father成员变量");

    Father()
    {
        cout << "Father 构造函数被调用了" << endl;
    }

    ~Father()
    {
        cout << "Father 析构函数被调用了" << endl;
    }
};

Value Father::s_value = Value("静态FatherValue被创建了");


class Son:public Father
{
public:
    static Value s_value;
    Value val = Value("Son成员变量");   // int i = int(10);
    Son()
    {
        cout << "Son 构造函数被调用了" << endl;
    }

    ~Son()
    {
        cout << "Son 析构函数被调用了" << endl;
    }
};

Value Son::s_value = Value("静态SonValue被创建了");

int main()
{
    cout << "主函数开始执行" << endl;
    {   // 局部代码块
        Son s;
        cout << "对象执行中" << endl;
    }
    cout << "主函数结束了" << endl;
    return 0;
}

上面的执行结果中,得到那些规律?

● 以对象执行中 为轴,上下对称。

● 在创建的过程中,同类型的内存区域,基类先开辟。对象创建:(先基类后派生类)。对象销毁(先派生类,后基类)。

● 静态的创建早于非静态。

1.4 多重继承(掌握)

1.4.1 概念

C++支持多重继承,即一个派生类可以有多个基类。派生类对于每个基类的关系仍然可以看作是一个单继承。

#include <iostream>

using namespace std;


class Sofa
{
public:
    void sit()
    {
        cout << "沙发可以坐着" << endl;
    }
};

class Bed
{
public:
    void lay()
    {
        cout << "床可以躺着" << endl;
    }
};

// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};


int main()
{
    SofaBed sb;
    sb.lay();
    sb.sit();
    return 0;
}

1.4.2 可能出现的问题

1.4.2.1 问题1-重名问题(掌握)

当多个基类具有重名成员时,编译器在编译的过程中会出现二义性的问题。

解决方式:使用基类的类名::方式调用。

#include <iostream>

using namespace std;


class Sofa
{
public:
    void sit()
    {
        cout << "沙发可以坐着" << endl;
    }

    void clean()
    {
        cout << "打扫沙发" << endl;
    }
};

class Bed
{
public:
    void lay()
    {
        cout << "床可以躺着" << endl;
    }

    void clean()
    {
        cout << "打扫床" << endl;
    }
};

// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};


int main()
{
    SofaBed sb;
    sb.lay();
    sb.sit();
    sb.Sofa::clean();
    sb.Bed::clean();
    return 0;
}
1.4.2.2 问题2-菱形继承(熟悉)

当一个基类有多个派生类,且这些派生类又有一个共同基类时。就会出现二义性问题,这种继承方式称为菱形(钻石)继承。

有两种解决方式:

1、使用基类的类名::方式调用

#include <iostream>

using namespace std;

// 家具厂
class Furniture
{
public:
    void func()
    {
        cout << "家具厂里有家具" << endl;
    }
};

class Sofa:public Furniture
{
public:

};

class Bed:public Furniture
{
public:

};

// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};


int main()
{
    SofaBed sb;
    sb.Bed::func();
    sb.Sofa::func();

    return 0;
}
2、使用虚继承(熟悉)

当出现虚继承时,Furniture类会生成一张虚基类表。这个表并不会占用任何对象的存储空间,属于Furniture类持有,在程序启动时加载进内存,表中记录了Furniture函数的调用地址偏移量。

Bed和Sofa对象会出现一个隐藏的成员变量指针,指向Furniture类中的虚基类表,占用对象四个字节。

SofaBed继承Sofa和Bed时,SofaBed类对象会同时拥有两个虚基类表指针成员,在调用时通过查表解决二义性。

#include <iostream>

using namespace std;

// 家具厂
class Furniture
{
public:
    void func()
    {
        cout << "家具厂里有家具" << endl;
    }
};

class Sofa:virtual public Furniture
{
public:

};

class Bed:virtual public Furniture
{
public:

};

// 多重继承
class SofaBed :public Sofa,public Bed
{
public:
};


int main()
{

    SofaBed sb;
    sb.func();


    return 0;
}

二.权限

类内

派生类中

全局

private

×

×

protected

×

public

2.1 公有继承

上面的代码中一直使用的就是公有继承,公有继承也是使用最多的一种继承方式。

在公有继承中,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类的公有成员与保护成员在派生类中权限不变。

2.2 保护继承

在保护继承中,派生类可以继承基类的成员,不可以访问基类的私有成员,基类的公有成员与保护成员在派生类中都是保护权限。(只能在基类和派生类中访问,外部无法访问)。

2.3 私有继承

在私有继承中,派生类可以继承基类的成员,但是不可以访问基类的私有成员,基类的公有成员与保护成员在派生类中权限都是私有权限。

#include <iostream>

using namespace std;


class Base
{
private:
    string str1 = "私有成员";
protected:
    string str2 = "保护成员";
public:
    string str3 = "公有成员";
};

class Son:private Base
{
public:
    Son()
    {
//        cout << str1 << endl; // 错误str1为私有成员,派生类中无法访问
        cout << str2 << endl;
        cout << str3 << endl;
    }
};

class Sun:public Son
{
public:
    Sun()
    {
//        cout << str1 << endl; // 错误str1为私有成员,派生类中无法访问
//        cout << str2 << endl; // 错误str2 在Son类中是私有成员,派生类无法访问
//        cout << str3 << endl; // 错误str3 在Son类中是私有成员,派生类无法访问
    }
};

int main()
{
    Sun s1;
//    cout << s1.str1 << endl; // 错误,私有权限类外无法访问
//    cout << s1.str2 << endl; // 错误,私有权限类外无法访问
//    cout << s1.str3 << endl; // 错误,私有权限类外无法访问
    return 0;
}

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

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

相关文章

为什么SEO是一个不断学习和适应的过程?

SEO并不是一成不变的&#xff0c;它是一个需要不断学习和适应的过程。谷歌的算法经常更新&#xff0c;用户的搜索行为也在不断变化&#xff0c;这使得SEO策略必须与时俱进&#xff0c;才能保持有效性。企业需要认识到&#xff0c;SEO是一项长期的投资&#xff0c;需要持续的关注…

Spring WebFlux 响应式概述(1)

1、响应式编程概述 1.1、响应式编程介绍 1.1.1、为什么需要响应式 传统的命令式编程在面对当前的需求时的一些限制。在应用负载较高时&#xff0c;要求应用需要有更高的可用性&#xff0c;并提供低的延迟时间。 1、Thread per Request 模型 比如使用Servlet开发的单体应用&a…

PostgreSQL学习笔记十:锁机制详解

一、PostgreSQL 的锁机制 PostgreSQL中的锁机制是确保数据一致性和完整性的关键。它通过不同级别的锁来控制对数据对象的并发访问&#xff0c;主要包括表级锁、行级锁、页级锁、咨询锁&#xff08;Advisory Locks&#xff09;以及死锁&#xff08;Deadlocks&#xff09;。 1. …

基于Java+Springboot+Vue开发的大学竞赛报名管理系统

项目简介 该项目是基于JavaSpringbootVue开发的大学竞赛报名管理系统&#xff08;前后端分离&#xff09;&#xff0c;这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能&#xff0c;同时锻炼他们的项目设计与开发能力。通过学习基于Java…

# linux从入门到精通-从基础学起,逐步提升,探索linux奥秘(九)--网络设置与文件上传下载

linux从入门到精通-从基础学起&#xff0c;逐步提升&#xff0c;探索linux奥秘&#xff08;九&#xff09;–网络设置与文件上传下载 一、网络设置 1、首先知道网卡配置文件位置&#xff1a;/etc/sysconfig/network-scripts [rootlocalhost test1]# ls /etc/sysconfig/netwo…

JSON 格式化工具:快速便捷地格式化和查看 JSON 数据

JSON 格式化工具&#xff1a;快速便捷地格式化和查看 JSON 数据 为什么需要 JSON 格式化工具&#xff1f; 在日常开发和调试中&#xff0c;JSON 是非常常见的数据交换格式。无论是前端与后端的接口调用&#xff0c;还是数据存储和处理&#xff0c;JSON 格式都扮演着重要角色。…

【HarmonyOS开发笔记 2 】 -- ArkTS语法中的变量与常量

ArkTS是HarmonyOS开发的编程语言 ArkTS语法中的变量 【语法格式】&#xff1a; let 变量名: 类型 值 let&#xff1a;是定义变量的关键字类型&#xff1a; 值数据类型&#xff0c; 常用的数据类型 字符型&#xff08;string&#xff09;、数字型&#xff08;number&#xf…

PG 17 增量备份功能介绍

背景 PG 17 新增了增量备份功能&#xff0c;可以通过 pg_basebackup --incrementalPATH_TO_MANIFEST 命令进行增量备份。 官方文档&#xff1a;https://www.postgresql.org/docs/current/app-pgbasebackup.html 使用方法 全量备份 启动实例后&#xff0c;首先配置参数 sum…

【北京迅为】《STM32MP157开发板嵌入式开发指南》- 第三十五章 嵌入式开发概述及环境构建

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…

数据库表使用方法:

数据库表&#xff1a; 提供四种类型的约束保证完整性&#xff1a; 1.域完整性&#xff0c;2.实体完整性 3.自定义完整性 4.引用完整性 实体完整性&#xff1a; 约束方法&#xff1a;唯一约束&#xff0c;主键约束&#xff0c;标识列 域完整性&#xff1a; 约束方法&#x…

基于depth anything模型理解深度估计运行机理

文章目录 前言一、概念说明1、深度概念2、绝对深度概念3、相对深度概念4、深度估计表示方法二、相对深度估计与绝对(即度量)深度估计1、模型预测绝对深度劣势与应用优势2、模型预测相对深度必然性3、小结三、深度估计模型loss方法四、深度估计模型评估方法1、绝对相对误差2、…

Python:方法的链式调用

相关阅读 Pythonhttps://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 在Python编程中&#xff0c;方法的链式调用是一种简洁且优雅的编程风格&#xff0c;它允许你在一行代码中连续调用多个方法&#xff0c;这种编程模式在简化代码、提升可…

22.安卓逆向-frida基础-objection工具1-安装和简单使用(Python的pip指令安装完提示不是内部命令解决办法)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

java4~~~

日期 第一代 import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.SimpleFormatter;public class Main {public static void main(String[] args) throws ParseException {//两种构造器的使用//1、获取当…

OpenCV高级图形用户界面(1)创建滑动条函数createTrackbar()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 创建一个滑动条并将其附加到指定的窗口。 该函数 createTrackbar 创建一个具有指定名称和范围的滑动条&#xff08;滑块或范围控制&#xff09;…

Flexbox 弹性盒子布局

Flexbox&#xff0c;全称弹性盒子布局&#xff0c;提供更精细的控制&#xff0c;能轻松解决困扰我们许久的垂直居中和登高列问题。 1 display: flex 将容器设置为弹性容器&#xff0c;容器会占据100%的可用宽度&#xff0c;高度则由自身的内容来决定&#xff0c;即使改变主轴…

基于SSM的“企业人事管理系统”的设计与实现(源码+数据库+文档)

基于SSM的“企业人事管理系统”的设计与实现&#xff08;源码数据库文档) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登陆页面 部门管理页面 加班页面 考勤页面 请假页面 工资页面 …

leetcode链表(三)-反转链表

题目 . - 力扣&#xff08;LeetCode&#xff09; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 思路 首先定义一个cur指针&#xff0c;指向头结点&#xff0c;再定义一个pre指针&#xff0c;初始化为None。 然后就要开始反转了&…

使用tgz包下载安装clickhouse低版本

1.下载安装包 官方下载地址&#xff1a;https://packages.clickhouse.com/tgz/stable 阿里云下载地址&#xff1a;clickhouse-tgz-stable安装包下载_开源镜像站-阿里云 共需要下载四个文件 clickhouse-common-static-20.3.10.75.tgz clickhouse-common-static-dbg-20.3.10.7…

如何通过零工市场小程序提高匹配效率?

零工市场正往好的方向逐步发展&#xff0c;零工市场小程序就是数字化转型成功的标志&#xff0c;那么零工市场小程序作为求职者和雇主之间沟通的桥梁&#xff0c;通过利用现代技术&#xff0c;例如Java算法&#xff0c;提高了灵活就业市场的效率。 Java通过数据分析&#xff0…