C++核心编程---面向对象的三大特性---继承

C++核心编程—面向对象的三大特性—继承

文章目录

      • C++核心编程---面向对象的三大特性---继承
        • 1. 基本继承语法
        • 2. 继承方式
        • 3. 多重继承
        • 4. 构造和析构顺序
          • 4.1 构造函数的调用顺序:
          • 4.2 析构函数的调用顺序:
        • 5. 虚继承
        • 6. 访问基类成员
        • 7. 同名成员和同名静态成员的处理
          • 1. 成员变量的处理
          • 2. 成员函数的处理
          • 3. 静态成员的处理
          • 4. 示例代码

C++中的继承是面向对象编程的一个核心特性,它允许我们创建一个类(派生类),该类可以从已有的类(基类)中继承属性和行为。

1. 基本继承语法

在C++中,使用冒号(:)和访问修饰符来表示继承关系。访问修饰符可以是publicprotectedprivate,它们定义了基类成员在派生类中的可访问性。

class 基类名 {// 基类的成员
};class 派生类名 : 访问修饰符 基类名 {// 派生类的成员
};//例如:
//
// Created by 86189 on 2024/6/21.
//
#include <iostream>
#include <utility>using namespace std;class Base {
public:int age{};int sex{};string name = " ";string phone = " ";
};class Student : public Base {
public:string job;Student(string name, int age, int sex, string phone, const string& job) {this->name = std::move(name);this->age = age;this->sex = sex;this->phone = std::move(phone);this->job = job;}
};ostream &operator<<(ostream &out, Student &s) {out << "姓名:" << s.name << " 年龄:" << s.age << " 性别:" << s.sex << " 电话:" << s.phone << " 职业:" << s.job;return out;
}class Teacher : public Base {
public:string job;Teacher(string name, int age, int sex, string phone, const string& job) {this->name = std::move(name);this->age = age;this->sex = sex;this->phone = std::move(phone);this->job = job;}
};ostream &operator<<(ostream &out, Teacher &t) {out << "姓名:" << t.name << " 年龄:" << t.age << " 性别:" << t.sex << " 电话:" << t.phone << " 职业:" << t.job;return out;
}int main() {Student s("张三", 18, 1, "123456789", "学生");Teacher t("李四", 18, 1, "123456789", "教师");cout << s << endl;cout << t << endl;return 0;
}
2. 继承方式
  • public继承:基类的公有成员在派生类中仍为公有;保护成员变为派生类的保护成员;私有成员对派生类不可见,但影响派生类的布局。
  • protected继承:基类的公有和保护成员都变为派生类的保护成员;私有成员对派生类不可见。
  • private继承:基类的所有公有和保护成员都变为派生类的私有成员;私有成员对派生类不可见。
//
// Created by 86189 on 2024/6/21.
//
#include <iostream>using namespace std;class Base{
public:int age;
protected:int height;
private:int weight;
};class Son:public Base{
public:Son(int a,int b) : Base(){age = a;height = b;}
};
class Son2:private Base{
public:Son2(int a,int b) : Base(){age = a;height = b;}
};class Son3:protected Base{
public:Son3(int a) : Base(){age = a;//weight = b;  无法访问}
};int main(){Son s(10,20);Son2 s2(10,20);Son3 s3(10);cout<<s.age<<endl;
//    cout<<s2.age<<endl;  保护成员 无法访问
//    cout<<s3.age<<endl;  私有成员 无法访问return 0;
}

注意:从父类继承的属性同样位于子类上。

3. 多重继承

C++还支持一个派生类从多个基类继承,这称为多重继承。在多重继承的情况下,访问修饰符需要分别指定。

class 派生类名 : 访问修饰符 基类名1, 访问修饰符 基类名2, ... {// 派生类的成员
};//
// Created by 86189 on 2024/6/21.
//#include <iostream>using namespace std;class Base1 {
public:int a;static void func1() {cout << "Base1::func1" << endl;}
};class Base2 {
public:int a;static void func2() {cout << "Base2::func2" << endl;}
};class Derived : public Base1, public Base2 {
public:int b;Derived(int a, int b,int c) : Base1(), Base2() {Base1::a = a;this->b = b;Base2::a = c;}static void func3() {cout << "Derived::func3" << endl;}
};int main() {Derived d(1, 2,3);cout << d.Base1::a << endl;cout << d.Base2::a << endl;Derived::func1();Derived::func2();Derived::func3();cout << d.b << endl;return 0;
}
4. 构造和析构顺序

在C++中,继承涉及的构造函数和析构函数调用顺序遵循以下规则:

4.1 构造函数的调用顺序:
  1. 基类构造函数:当创建派生类的对象时,首先会调用基类的构造函数。如果有多个基类(多继承情况),则按照派生类定义时基类出现的顺序调用。每个基类构造函数按其自身的参数列表进行初始化。

  2. 成员对象构造函数:接下来,按照它们在派生类中声明的顺序,调用派生类中任何非静态成员对象的构造函数。

  3. 派生类构造函数:最后,调用派生类自身的构造函数体。这里可以进一步初始化派生类新增的成员变量。

4.2 析构函数的调用顺序:

析构函数的调用顺序与构造函数的调用顺序相反:

  1. 派生类析构函数:当派生类对象生命周期结束时,首先执行派生类的析构函数体,释放派生类特有的资源。

  2. 成员对象析构函数:接着,按照它们在派生类中声明的逆序,调用派生类中非静态成员对象的析构函数。

  3. 基类析构函数:最后,按照派生类继承列表中基类出现的逆序调用基类的析构函数,释放基类的资源。

总结来说,构造函数是从基类到派生类、从父到子的方向进行初始化,而析构函数则是从派生类到基类、从子到父的方向进行资源的清理。这样的设计保证了资源的正确分配和释放。

5. 虚继承

虚继承是用来解决多重继承中可能出现的二义性和数据冗余问题的。当一个类作为多个派生类的基类时,如果每个派生类都包含基类的一个完整副本,这将导致存储空间的浪费。通过在派生类声明时使用virtual关键字,可以实现虚继承。

class 基类名 {// ...
};class 派生类1 : virtual public 基类名 {// ...
};class 派生类2 : virtual public 基类名 {// ...
};//
// Created by 86189 on 2024/6/21.
//
#include <iostream>using namespace std;class Base{
public:int age;
};class Son1 : virtual public Base{
public:int sex{};
};class Son2 : virtual public Base{
public:int high{};
};class GrandSon : public Son1 , public Son2{
public:int weight{};
};int main(){Son1 s1;Son2 s2;s1.age = 18;s2.age = 20;cout << s1.age << endl;cout << s2.age << endl;GrandSon g;g.age = 20;cout << g.age << endl;return 0;
}
6. 访问基类成员

在派生类中,可以直接访问基类的公有和受保护成员(根据继承的访问权限)。如果需要明确地指明成员来自哪个基类,可以使用基类名::操作符。

class Base {
public:void baseFunction() { /*...*/ }
};class Derived : public Base {
public:void derivedFunction() {baseFunction(); // 直接调用Base::baseFunction(); // 明确指定基类}
};
7. 同名成员和同名静态成员的处理

在C++继承中,如果派生类(子类)和基类(父类)拥有同名的成员(包括数据成员和成员函数),可以通过以下几种方式来解决同名成员的访问问题:

1. 成员变量的处理
  • 优先级:如果派生类中有一个与基类同名的数据成员,那么通过派生类对象访问该同名成员时,将优先访问派生类的成员变量。
  • 访问基类成员:如果需要访问被派生类同名成员隐藏的基类成员,可以使用作用域解析运算符 :: 明确指定基类的作用域。例如,baseClass::memberName
2. 成员函数的处理
  • 覆盖(Overriding)与隐藏:如果派生类定义了一个与基类同名的成员函数,这可能构成重写(如果是虚函数)或隐藏(如果是非虚函数或参数列表不同)。对于虚函数,派生类会覆盖基类的同名函数,而对于非虚函数或签名不同的函数,则是隐藏基类的同名函数。
  • 访问基类函数:如果要访问被派生类同名函数隐藏的基类函数,同样需要使用作用域解析运算符,如 baseClass::functionName()
  • 注意重写的规则:重写要求函数签名(除了返回类型外的参数列表)必须与基类中的虚函数完全匹配,并且访问权限不能比基类中的更严格。
3. 静态成员的处理
  • 静态成员的处理方式与非静态成员类似,即派生类可以重新定义同名的静态成员,但访问时需明确指定是基类还是派生类的静态成员。
4. 示例代码
class Base {
public:int var = 10;void display() { cout << "Base's var: " << var << endl; }
};class Derived : public Base {
public:int var = 20;void display() { cout << "Derived's var: " << var << endl; }void accessBaseVar() {cout << "Accessing Base's var: " << Base::var << endl; // 明确访问基类的var}void callBaseDisplay() {Base::display(); // 明确调用基类的display函数}
};int main() {Derived d;d.display(); // 输出 Derived's var: 20d.accessBaseVar(); // 输出 Accessing Base's var: 10d.callBaseDisplay(); // 输出 Base's var: 10return 0;
}

这段代码展示了如何在派生类中处理同名的成员变量和成员函数,以及如何通过作用域解析运算符访问基类的同名成员。

同时:需要注意的是,当基类的成员函数有重载时,需要指定基类的作用域才可以访问到发生了重载的函数。

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

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

相关文章

第二十四节:带你梳理Vue2 : Vue具名插槽/作用域插槽/v-slot指令

1. 具名插槽 1.1 没有使用具名插槽的问题 有的时候我们在使用子组件时,在子组件模板上不同的位置插入不同的内容, 只有一个插槽显然没法满足我们的需求,看示例: 需求如下: 子组件是一篇文章的结构父组件在调用子组件是给文章插入标题,正文,时间信息 示例代码如下: <di…

【强化学习的数学原理】课程笔记--1(基本概念,贝尔曼公式)

目录 基本概念State, Action, State transitionPolicy, Reward, Trajectory, Discount ReturnEpisodeMarkov decision process 贝尔曼公式推导确定形式的贝尔曼公式推导一般形式的贝尔曼公式State ValueAction Value 一些例子贝尔曼公式的 Matric-vector form贝尔曼公式的解析解…

Elasticsearch 索引与文档操作实践指南

上节我们部署了elasticsearch8.4.1和kibana8.4.1以及ik分词器&#xff0c;本节我们来学习下elasticsearch的相关索引&#xff0c;文档操作。 首先我们kinana的主界面找到开发者工具在里面我们来编写相关操作。 标题查看集群情况 GET /_cluster/health详细解释&#xff1a; …

心理学|发展心理学——发展心理学单科作业(中科院)

一、单选题(第1-100小题,每题0.5分,共计50分。) 1、认为心理发展是主体与客体相互作用的结果,持该观点的学者是( ) 分值0.5分 A、皮亚杰 B、普莱尔 C、吴伟士 D、施太伦 A 2、艾里克森认为童年期(7岁~12岁)的主要发展任务是( ) 分值0.5分 A、获得勤…

(四十四)Vue Router的命名路由和路由组件传参

文章目录 命名路由组件传参query参数方式参数传递参数接收 params参数方式参数传递参数接收 props配置方式布尔值形式对象模式函数模式 上一篇&#xff1a;&#xff08;四十三&#xff09;Vue Router之嵌套路由 命名路由 命名路由是为路由配置项提供一个名称&#xff0c;以便…

云计算【第一阶段(18)】磁盘管理与文件系统 分区格式挂载(一)

目录 一、磁盘基础 二、磁盘结构 2.1、机械硬盘 2.2、固态硬盘 2.3、扩展移动硬盘 2.4、机械磁盘的一些计算&#xff08;了解&#xff09; 2.5、磁盘接口类型 二、Linux 中使用的文件系统类型 2.1、磁盘分区的表示 2.1.1、主引导记录(MBR) 2.1.2、Linux中将硬盘、分…

【十二】图解 Spring 核心数据结构:BeanDefinition

图解 Spring 核心数据结构&#xff1a;BeanDefinition 简介 使用spring框架的技术人员都知道spring两个大核心技术IOC和AOP&#xff0c;随着投入更多的时间去学习spring生态&#xff0c;越发觉得spring的发展不可思议&#xff0c;一直都是引领着Java EE的技术变革&#xff0c;这…

麒麟信安系统关闭core文件操作

在使用麒麟信安系统时&#xff0c;如果应用程序运行过程中崩溃了&#xff0c;此时并不会导致内核崩溃&#xff0c;只会在tmp目录下产生崩溃数据&#xff0c;如下图 不过tmp目录下的分区容量有限&#xff0c;当崩溃的应用core文件过大时将会占用tmp空间&#xff0c;导致tmpfs分区…

上海计算机学会2022年6月月赛C++丙组T4连续的零

题目描述 给定一个 01 序列 b1​b2​…bn​&#xff0c;01 的意思就是这个数列里只有 0 与 1。 请问最少需要将多少个 1 改成 0&#xff0c;序列里会出现至少 k 个连续的 0。 输入格式 第一行&#xff1a;两个整数 n 与 k。第二行&#xff1a;n 个字符表示 b1​b2​…bn​&…

Msql----表的约束

提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、表的约束 表的约束&#xff1a;表中一定要有约束&#xff0c;通过约束让插入表中的数据是符合预期的。它的本质是通过技术手段&#xff0c;让程序员插入正确的数据&#xff0c;约束的最终目标是保证…

IP地址的主要功能

IP地址&#xff0c;作为互联网协议&#xff08;Internet Protocol&#xff09;的核心组成部分&#xff0c;是网络设备在互联网中的唯一标识。它不仅仅是一个简单的数字串&#xff0c;而是承载着网络通信的重要功能。以下是IP地址的主要功能及其在网络中的重要性的详细分析。 唯…

NAPI篇【4】——NAPI应用点亮一个LED

OpenHarmony的NAPI功能为开发者提供了JS与C/C不同语言模块之间的相互访问&#xff0c;交互的能力&#xff0c;使得开发者使用C或者C语言实现应用的关键功能。如操作开发板中某个GPIO节点的状态&#xff08;OpenHarmony并没有提供直接操作GPIO口状态的API&#xff09;&#xff0…

深入解读Netty中的NIO:原理、架构与实现详解

深入解读Netty中的NIO&#xff1a;原理、架构与实现详解 Netty是一个基于Java的异步事件驱动网络应用框架&#xff0c;广泛用于构建高性能、高可扩展性的网络服务器和客户端&#xff08;学习netty请参考&#xff1a;深入浅出Netty&#xff1a;高性能网络应用框架的原理与实践&…

vue2加入keep-alive后的生命周期情况

目录 1.加入keep-alive会执行哪些生命周期&#xff1f;1.activated2.deactivated 2.keep-alive是什么 &#xff1a; 缓存当前组件3.如果当前组件加入了keep-alive第一次进入这个组件会执行5个生命周期4.第二次或者第N次进去组件会执行哪些生命周期&#xff1f;1.如果当前组件加…

AIGC:引领内容创作新时代的智能引擎

随着人工智能技术的飞速发展&#xff0c;AIGC&#xff08;Artificial Intelligence Generated Content&#xff0c;人工智能生成内容&#xff09;逐渐崭露头角&#xff0c;成为推动内容创作领域变革的重要力量。作为一名程序员&#xff0c;我深感AIGC的巨大潜力和广阔前景&…

【Flink metric(3)】chunjun是如何实现脏数据管理的

文章目录 一. 基础逻辑二. DirtyManager1. 初始化2. 收集脏数据并check3. 关闭资源 三. DirtyDataCollector1. 初始化2. 收集脏数据并check3. run&#xff1a;消费脏数据4. 释放资源 四. LogDirtyDataCollector 一. 基础逻辑 脏数据管理模块的基本逻辑是&#xff1a; 当数据消…

猫咪也怕油腻?选对猫粮是关键!福派斯鲜肉猫粮守护猫咪健康

亲爱的猫友们&#xff0c;我们都知道&#xff0c;猫咪的饮食健康是每一个铲屎官都非常关心的问题。最近&#xff0c;有些猫友向我反映&#xff0c;他们给猫主子喂食的猫粮油脂比较大&#xff0c;不禁让人担心这对猫咪是否真的好。 1️⃣ 首先&#xff0c;让我们来聊聊油脂在猫粮…

Spring Boot + Vue 全栈开发,都需要哪些前端知识?

Node.js默认安装的npm包和工具的位置&#xff1a;Node.js目录\node_modules 在这个目录下你可以看见 npm目录&#xff0c;npm本身就是被NPM包管理器管理的一个工具&#xff0c;说明 Node.js已经集成了npm工具 #在命令提示符输入 npm -v 可查看当前npm版本 npm -v 二、使用n…

go多路复用功能

大家在工作中使用go语言都是因为go语言中提供方便又简介的协成吧&#xff0c;只需一个go关键字就可以开启一个协成&#xff0c;今天为大家推荐一种go实现的多路复用代码&#xff0c;大家可以参考一下呀可以下方评论留言呦 package test_7import ("fmt""testing…

如何在Ubuntu的docker部署web应用

1. 安装Docker和Docker Compose 首先&#xff0c;先介绍一下Docker与Docker Compose。 1、Docker&#xff1a; 功能&#xff1a;Docker是一个开源的容器化平台&#xff0c;用于开发、打包和运行应用程序。它利用容器技术&#xff08;如Docker容器&#xff09;来打包应用及其…