目录

访问权限:

继承:

示例:

构造和析构顺序:

多态:

示例:

非虚函数重写:

虚函数:

示例:

纯虚函数:


继承是C++中面向对象编程的核心特性之一,允许派生类继承基类的成员变量和成员函数,从而实现代码的重用和层次化设计。

继承方式与访问控制:

public:共有权限,类内类外都能够进行访问。

protected:保护权限,类内、友元和派生类能够进行访问。

private:私有权限,类内、友元能够进行访问。

public继承:基类的public、protected和private成员在派生类中都保持原有权限。

protected继承:基类的public和protected在派生类中为protected权限,private在派生类中还是保持原有权限。

private继承:基类的public、protected在派生类中都为private权限,private成员不可访问。

如果不想让一个类被继承,使用final关键字声明该类不能被继承。

访问权限:

示例:

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_public(0),A_protected(0),A_private(0){}A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){}
public:int A_public;
protected:int A_protected;
private:int A_private;
};int main() {A a;std::cout << a.A_public << std::endl;std::cout << a.A_protected << std::endl;std::cout << a.A_private << std::endl;return 0;
}

运行结果:

可以知道如果想直接在类外直接访问protected和private成员是无法访问的。

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_public(0),A_protected(0),A_private(0){}A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){}void Get() {std::cout << A_protected << " " << A_private << std::endl;}
public:int A_public;
protected:int A_protected;
private:int A_private;
};int main() {A a(1,2,3);std::cout << a.A_public << std::endl;a.Get();return 0;
}

运行结果:

通过成员函数来进行访问类中的protected和private成员。

#include <iostream>
#include <string>
#include <thread>class A {friend void Get(const A& other);
public:A():A_public(0),A_protected(0),A_private(0){}A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){}
public:int A_public;
protected:int A_protected;
private:int A_private;
};void Get(const A& other) {std::cout << other.A_public << " " << other.A_protected << " " << other.A_private << std::endl;
}int main() {A a(1,2,3);std::cout << a.A_public << std::endl;Get(a);return 0;
}

运行结果:

通过friend关键字在类中声明一个友元函数,在类外进行定义之后,来对类内的protected和private进行一个访问,public成员也能够对其进行访问。

继承:

当一个派生类继承基类时,派生类继承基类的所有成员变量和成员函数,但是根据继承的方式,对于基类的成员变量或者成员函数在派生类中有一个访问权限的转换。需要注意的是无论是public、protected或者private继承,只会影响基类的public和protected成员在派生类的权限,而基类的private成员在派生类中不可见,不可以直接对其进行访问,只能通过基类的方法对其进行访问。

示例:

#include <iostream>
#include <string>
#include <thread>class A {//friend void Get(const A& other);
public:A():A_public(0),A_protected(0),A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){std::cout << "类A的有参构造" << std::endl;}
public:int A_public;
protected:int A_protected;
private:int A_private;
};class B :public A {};class C :protected A {};class D :private A {};int main() {B b;C c;D d;std::cout << b.A_public << std::endl;//std::cout << c.A_public << std::endl;//std::cout << d.A_public << std::endl;return 0;
}

运行结果:

类B、C和D分别是public、protected和private继承类A,同时三个类都未提供构造函数。但是通过运行结果可以知道都调用了类A的无参构造函数。这里需要注意的是,并不是派生类继承了基类的构造函数,派生类并不会继承基类的任何构造函数,而是派生类中没有定义构造函数,编译器会自动生成一个默认构造函数,该函数自动调用了基类的默认构造函数。其中类B是public继承所以能够访问基类A的public成员,而类C、D分别是protected和private继承,基类A的public成员分别转换成protected权限和private权限,所以不能够直接进行访问。

构造和析构顺序:

#include <iostream>
#include <string>
#include <thread>class A {friend void Get(const A& other);
public:A():A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_private):A_private(A_private){std::cout << "类A的有参构造" << std::endl;}~A() {std::cout << "类A的析构函数" << std::endl;}
private:int A_private;
};void Get(const A& other) {std::cout << other.A_private << std::endl;
}class C {
public:C() {std::cout << "类C的无参构造" << std::endl;}~C() {std::cout << "类C的析构函数" << std::endl;}
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}
private:int B_private;C c;
};int main() {B b(3);return 0;
}

运行结果:

类B继承类A,然后在类B中定义一个成员变量c,c的类型是类C。那么通过上述运行结果,可以看出,先对基类A进行构造,然后再对派生类B的成员变量进行构造,这里需要注意的是,是按照成员变量的生命顺序来进行构造,最后才是对派生类本身类B进行构造。

析构的顺序,从运行结果不难看出,析构顺序与构造顺序相反,先析构派生类,然后对派生类的成员变量按照生命顺序从后往前进行析构,最后对基类进行析构。

多态:

多态是面向对象编程的三大特性之一,指统一操作作用于不同对象时产生不同的行为。

示例:

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_private):A_private(A_private){std::cout << "类A的有参构造" << std::endl;}~A() {std::cout << "类A的析构函数" << std::endl;}void Get() {std::cout << "类A的Get()函数" << std::endl;}
private:int A_private;
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}/*void Get() {std::cout << "类B的Get()函数" << std::endl;}*/
private:int B_private;
};int main() {B b;b.Get();return 0;
}

运行结果:

类B继承类A,创建一个类B然后进行调用Get()函数,通过运行结果可以知道,调用了基类中的Get()成员函数。那么如果在派生类B中也进行定义一个成员函数Get(),那么会调用基类的还是派生类的Get()函数?

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_private):A_private(A_private){std::cout << "类A的有参构造" << std::endl;}~A() {std::cout << "类A的析构函数" << std::endl;}void Get() {std::cout << "类A的Get()函数" << std::endl;}
private:int A_private;
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}void Get() {std::cout << "类B的Get()函数" << std::endl;}
private:int B_private;
};int main() {B b;b.Get();return 0;
}

运行结果:

从结果可以看出,在派生类定义了一个和基类一样的成员函数,那么再通过派生类进行调用该函数,不再调用基类的Get()成员函数,而是派生类中的Get()成员函数。

非虚函数重写:

派生类中定义与基类中同名的非虚函数时,会隐藏掉基类的同名函数实现。非虚函数采用的时静态绑定,再编译时就确定调用。当派生类对象直接调用该函数时,编译器会优先匹配派生类版本,而忽略基类的实现。如果想在派生类中进行使用基类的同名函数需要使用基类的作用域来进行调用。

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_private):A_private(A_private){std::cout << "类A的有参构造" << std::endl;}~A() {std::cout << "类A的析构函数" << std::endl;}void Get() {std::cout << "类A的Get()函数" << std::endl;}
private:int A_private;
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}void Get() {std::cout << "类B的Get()函数" << std::endl;}void Get_A() {A::Get();}
private:int B_private;
};int main() {B b;b.Get();b.Get_A();return 0;
}

运行结果:

虚函数:

虚函数是C++实现运行多态的关键机制,通过在基类中使用virtual关键字进行声明,允许派生类重写该函数实现。与上述非虚函数重写不同的是,当通过基类指针或者引用调用虚函数时,程序会根据实际对象类型动态决定调用。包含虚函数的类在编译时会生成一个虚函数表,存储该类的所有虚函数的地址。虚函数表是连续的内存结构,派生类会继承基类的虚函数表,并替换重写函数的地址,未重写的函数保留基类的实现。通过基类的指针或者引用调用虚函数时,编译器会生成一个虚函数表指针,通过该指针来进行定位函数地址之后进行动态绑定。

override关键字显示声明重写意图,编译器会对重写的函数进行检查是否书写正确。

基类可以使用final关键字防止派生类进一步重写该函数。

示例:

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_private):A_private(A_private){std::cout << "类A的有参构造" << std::endl;}~A() {std::cout << "类A的析构函数" << std::endl;}void Get() {std::cout << "类A的Get()函数" << std::endl;}virtual void func() {std::cout << "类A的func()函数" << std::endl;}
private:int A_private;
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}void Get() {std::cout << "类B的Get()函数" << std::endl;}void Get_A() {A::Get();}void func()override {std::cout << "类B的func()函数" << std::endl;}private:int B_private;
};int main() {A* p = new B();p->Get();p->func();delete p;return 0;
}

运行结果:

通过基类指针指向派生类对象的地址,如果是非虚函数调用的是基类的同名成员函数,但是如果是虚函数,那么调用的是派生类中的重写的虚函数。

从上述结果能够看出析构时,并没有调用派生类的析构函数,只调用了基类的析构函数。因为编译器根据指针的类型查找析构函数,因为是基类指针,所以直接调用基类析构跳过了派生类析构,导致基类释放而派生类未释放,就只会释放派生类中包含的基类,从而导致整体对象内存释放不完整。所以为了避免这种问题可以将基类的析构函数写为虚析构。

也就是基类指针指向派生类时,对基类指针进行释放时,如果积累的析构函数不是虚函数,只会调用基类的析构函数,导致派生类部分未被正确清理。

#include <iostream>
#include <string>
#include <thread>class A {
public:A():A_private(0){std::cout << "类A的无参构造" << std::endl;}A(int A_private):A_private(A_private){std::cout << "类A的有参构造" << std::endl;}virtual ~A() {std::cout << "类A的析构函数" << std::endl;}void Get() {std::cout << "类A的Get()函数" << std::endl;}virtual void func() {std::cout << "类A的func()函数" << std::endl;}
private:int A_private;
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}void Get() {std::cout << "类B的Get()函数" << std::endl;}void Get_A() {A::Get();}void func()override {std::cout << "类B的func()函数" << std::endl;}private:int B_private;
};int main() {A* p = new B();p->Get();p->func();delete p;return 0;
}

运行结果:

纯虚函数:

纯虚函数通过"=0"将虚函数声明为纯虚函数,纯虚函数在派生类中必须实现该函数。

如果一个类包含至少一个纯虚函数,那么该类就称为抽象类,抽象类不能够对其进行实例化。

#include <iostream>
#include <string>
#include <thread>class A {
public:virtual ~A() {std::cout << "类A的析构函数" << std::endl;}virtual void func() = 0 {std::cout << "类A的func()函数" << std::endl;}
};class B :public A {
public:B():B_private(0){std::cout << "类B的无参构造" << std::endl;}B(int B_private):B_private(B_private){std::cout << "类B的有参构造" << std::endl;}~B() {std::cout << "类B的析构函数" << std::endl;}void func()override {std::cout << "类B的func()函数" << std::endl;}private:int B_private;
};int main() {A* p = new B();p->func();delete p;return 0;
}

运行结果:

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

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

相关文章

07_Softmax回归、损失函数、分类

1. Softmax回归 ① Softmax回归虽然它的名字是回归&#xff0c;其实它是一个分类问题。2. 回归VS分类3. Kaggle分类问题4. 回归到分类5. 交叉熵损失6. 总结7. 损失函数 ① 三个常用的损失函数 L2 loss、L1 loss、Huber’s Robust loss。8. L2 Loss ① 蓝色曲线为当y0时&#xf…

UFO²:微软推出的新一代桌面 Agent 操作系统,深度整合 Windows 与智能自动化

本文转载自&#xff1a;https://www.hello123.com/ufo ** 一、微软 UFO&#xff1a;Windows 桌面智能体操作系统的革新与实战解析 &#x1f4bb; 微软 UFO&#xff08;Unified Functional Optimization Operating System&#xff09;是微软在 2025 年对其开源智能体系统 Agen…

C语言入门指南:字符函数和字符串函数

目录 前言&#xff1a; 一. 字符分类函数&#xff1a;精准识别字符的“身份” 1.1 ​​​​​​​核心函数 1.2 经典应用示例&#xff1a; 二、 字符转换函数&#xff1a;优雅地改变字符形态 三、strlen&#xff1a;计算长度的基石与无符号陷阱 3.1 关键特性 3.2 致命陷…

闪电科创-交通信号灯仿真SUMO

闪电科创计算机人工智sci/ei会议/ccf/核心&#xff0c;擅长机器学习&#xff0c;深度学习&#xff0c;神经网络&#xff0c;语义分割等计算机视觉&#xff0c;精通大小论文润色修改&#xff0c;代码复现&#xff0c;创新点改进等等

2025智能制造研发效率提升指南:从“项目-流程-数据”闭环看工具选型

一、引言&#xff1a;12年智能制造老兵的一线观察我在智能制造领域从业12年&#xff0c;先后主导过5家制造企业的研发流程数字化转型&#xff0c;从汽车零部件到高端装备制造&#xff0c;见证了太多研发团队因工具选型不当导致的效率损耗&#xff1a;项目进度卡在审批流程里、测…

spring中case一直返回else中的值-问题和原理详解

目录 案例背景 问题现象 问题根源 解决过程 最终结论 经验总结 案例背景 在基于 Spring Boot MyBatis 的项目中&#xff0c;需要通过 SQL 的 CASE WHEN 语句生成 user_Name字段&#xff08;表示是否有关联用户名称&#xff0c;1 为有关联&#xff0c;0 为无关联&#xf…

Apache IoTDB V1.3.5 发布|优化加密算法,优化内核稳定性,修复社区反馈问题

Release AnnouncementVersion 1.3.5 Apache IoTDB V1.3.5 已经发布&#xff01;V1.3.5 作为之前 1.3.x 的 bugfix 版本升级&#xff0c;主要调整用户密码加密算法&#xff0c;进一步强化数据访问安全&#xff0c;同时优化内核稳定性&#xff0c;修复社区反馈问题。欢迎点击阅读…

开源好用的博客系统简介和详细安装教程

目录 看效果 ① 搜索一键安装包 ② 填写安装信息 ③ 使用界面安装向导 ④ 安装完成 使用普通模式安装 看效果 下面直接来安装教程 ① 搜索一键安装包 登录宝塔后台系统&#xff0c;进入软件商店 → 一键部署&#xff0c;搜索 “ModStart”。 ② 填写安装信息 点击“一键…

医院高值耗材智能化管理路径分析(下)

医保协同:政策适配与编码联动的精准付费 国家医保局"带码采购、带码使用、带码结算"政策推动下,AI系统通过编码映射与实时规则引擎实现医保支付的动态适配。国医科技构建的UDI编码、医保编码与收费编码三码联动体系,可在耗材使用时自动匹配国家医保医用耗材分类与…

硬件开发2-ARM裸机开发1-I.MX6ULL - 汇编点灯

一、概念概要1、LED原理图2、内核中对应的引脚 — GPIO&#xff08;1&#xff09;概念GPIO&#xff08;通用输入/输出&#xff09;详解GPlO&#xff08;General-PurposeInput/Output&#xff09;是嵌入式系统和微控制器中最基本的外设接口&#xff0c;用于 实现数字信号的输入和…

Qwen3-80B-A3B混合注意力机制

一、注意力机制背景&#xff1a; 在Transformer架构中&#xff0c;自注意力&#xff08;Self-Attention&#xff09;是核心组件。其基本公式为&#xff1a; 其中&#xff1a; Q (Query)&#xff1a;查询向量&#xff0c;表示问询量。用于与其他位置的Key交互&#xff0…

数据库(一)数据库基础及MySql 5.7+的编译安装

文章目录前言一、数据库概述1.1 前置知识1.1.1 LAMP / LNMP 架构1.1.2 数据库的定位1.2 数据库基本概念1.2.1 数据1.2.2 表1.2.3 数据库1.2.4 数据库管理系统&#xff08;DBMS&#xff09;1.2.5 数据库系统&#xff08;DBS&#xff09;1.3 数据库发展史1.3.1 第一阶段&#xff…

Elasticsearch HTTPS访问错误解决指南

文章目录&#x1f50d; 原因分析✅ 正确的访问方式&#xff1a;使用 curl -k https://...&#x1f510; 你需要知道 elastic 用户的密码方法 1&#xff1a;查看首次生成的密码&#xff08;如果刚安装&#xff09;方法 2&#xff1a;重置密码✅ 成功示例&#x1f389; 总结&…

Neural ODE原理与PyTorch实现:深度学习模型的自适应深度调节

对于神经网络来说&#xff0c;我们已经习惯了层状网络的思维&#xff1a;数据进来&#xff0c;经过第一层&#xff0c;然后第二层&#xff0c;第三层&#xff0c;最后输出结果。这个过程很像流水线&#xff0c;每一步都是离散的。 但是现实世界的变化是连续的&#xff0c;比如…

Elasticsearch面试精讲 Day 16:索引性能优化策略

【Elasticsearch面试精讲 Day 16】索引性能优化策略 在“Elasticsearch面试精讲”系列的第16天&#xff0c;我们将深入探讨索引性能优化策略。这是Elasticsearch高频面试考点之一&#xff0c;尤其在涉及高并发写入、海量日志处理或实时数据分析场景时&#xff0c;面试官常通过…

ESP32-C3 入门09:基于 ESP-IDF + LVGL + ST7789 的 1.54寸 WiFi 时钟(SquareLine Studio 移植)

一. https://github.com/nopnop2002/esp-idf-st7789 1. 前言 2. 开发环境准备 2.1 硬件清单 ESP32-C3 开发板ST7789 1.54 寸 LCD其他辅助元件&#xff08;杜邦线、电源&#xff09; 2.2 软件安装 ESP-IDF 环境安装&#xff08;WindowsVScode&#xff09;VSCode 插件配置LV…

PINN物理信息神经网络驱动的三维声波波动方程求解MATLAB代码

MATLAB 代码实现了一个基于物理信息神经网络&#xff08;Physics-Informed Neural Network, PINN&#xff09;的三维波动方程求解器。以下是详细分析&#xff1a;&#x1f9e0; 一、主要功能&#x1f517; 二、逻辑关联 代码结构清晰&#xff0c;分为五个主要部分&#xff1a; …

leetcode33(最小栈)

设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。实现 MinStack 类:MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int getMin(…

TDesign学习:(二)i18n配置与使用

配置 src/locales/lang/en_US/pages 目录下对应的各个模块语言的对象

k8s 内置的containerd配置阿里云个人镜像地址及认证

原因&#xff1a;阿里云仓库必须使用凭证登录&#xff0c;不然无法进行镜像拉取&#xff01;1.生成自己的凭证信息# 格式&#xff1a;阿里云仓库用户名:凭证密码 echo -n myuser:mypass | base64 #生成的加密凭证 bXl1c2VyOm15cGFzcw2.修改containerd的镜像仓库配置vi /etc/co…