当前位置: 首页 > news >正文

【计算机基础理论知识】C++篇(一)

【计算机基础理论知识】C++篇(一)

在这里插入图片描述

🔥个人主页大白的编程日记

🔥专栏计算机基础理论知识


文章目录

  • 【计算机基础理论知识】C++篇(一)
    • 前言
  • 面向对象的三大特性
    • 封装
    • 继承
    • 多态
  • Volatile
    • 主要用途
      • 1. 编译器优化
      • 2. 多线程优化
      • 3. 硬件映射
  • 内存排布
    • 栈区
    • 堆区
    • 数据区
    • 代码区
    • 常量区
  • Static
    • 局部静态变量
    • 全局静态变量和函数
    • 类静态成员变量
    • 类静态成员函数
  • 四种智能指针 && 循环引用
    • auto_ptr(已废弃)
    • unique_ptr
    • shared_ptr
      • 原理
    • shared_ptr 的线程安全问题
    • weak_ptr
    • 循环引用
  • RAII
  • 构造函数和析构函数的虚函数问题
    • 构造函数不能是虚函数
    • 析构函数通常是虚函数,否则会导致内存泄漏
    • 后言

前言

哈喽,各位小伙伴大家好!今天我们来讲一下【计算机基础理论知识】C++篇(一)。话不多说,我们进入正题!向大厂冲锋
在这里插入图片描述

面向对象的三大特性

封装

定义:
封装是指将数据和方法绑定在一起,通过访问权限符对外部隐藏对象内存的实现细节,以接口的形式与外界交互。

作用:

  • 防止外部直接访问和修改对象的数据,提高代码的安全性和可靠性
  • 对象的数据和方法绑定在一起,模块直接以接口的方式交互,减低代码模块之间的耦合度
  • 提高代码的可读性和可维护性

继承

定义:
继承是指子类可以通过继承的方式继承父类的属性和方法,并且子类继承后可以复用父类的代码。子类也可以新增属性和方法或覆盖父类的方法。

作用:

  • 继承可以让子类直接复用父类的代码,减少冗余代码,代码的可读性和可维护性更好
  • 通过子类可以在父类的继承上扩展属性和方法,增强代码的可扩展性

多态

定义:
多态是指同一接口对不同对象做出不同响应,即同一个函数可以根据对象的不同具有不同的行为。

作用:

  • 多态可以使用同一的接口来处理不同的对象,无需关心具体对象的类型,提高代码的灵活性
  • 要增加新的功能时只需要定义新的类并实现接口即可,无需修改现有的代码,可扩展性强

Volatile

volatile 是一个用于修饰变量的关键字,表示该变量在程序运行过程中可能会被外部因素改变,相当于告诉编译器不要对该变量进行优化,不要把变量缓存在寄存器。每次访问变量都去内存中取出该变量的最新值,而不是去访问优化后缓存在寄存器中的值。


主要用途

1. 编译器优化

对于编译器的优化,编译器可能会把变量的值缓存在速度更快的寄存器中来减少对内存的访问,提高效率。但寄存器中的值是内存中值的一个副本。如果变量的值在内存中被其他因素(如硬件设备、其他线程等)修改了,而寄存器中的值没有更新,就会导致程序读取到过时的值。volatile 就是告诉寄存器每次访问都是读取内存中最新的值。

2. 多线程优化

在多线程程序中,一个变量被多线程访问时可能会被优化,volatile 可以用于确保变量的访问不会被线程优化掉。

3. 硬件映射

在嵌入式系统中,硬件设备的状态通常通过内存映射的寄存器来表示。这些寄存器的值可能会被硬件设备随时更新。程序需要能够及时检测到这些变化,并正确地与硬件设备进行交互。volatile 可以防止变量的值缓存在寄存器中,确保每次都能读取到内存中最新的值。


内存排布

栈区

  • 存储局部变量,如函数参数、函数地址等
  • 特点:后进先出,由操作系统自动管理分配,大小较小,通常为几M,高向低地址方向增长
  • 在函数调用后数据会自动销毁,操作系统提供了专门的寄存器存放栈的地址,压栈和出栈操作都有专门的指令,分配效率高

堆区

  • 存储动态分配的内存
  • 特点:由程序员通过 newmalloc 手动申请、管理和释放(deletefree),大小较大
  • 低向高地址方向增长,需要遍历空闲链表,记录分配大小,内存不连续
  • 频繁申请释放会导致内存碎片化,无法申请出更大空间,分配效率低

数据区

  • 分为初始化数据区(显示初始化)和未初始化数据区(BSS,默认初始化为0)
  • 存储全局变量和静态变量
  • 特点:程序的整个周期有效

代码区

  • 存储程序编译好后的机器指令(如 movcall
  • 特点:只读,大小固定,具有共享性
  • 例如动态库的代码加载一次就可以被调用的地方跳转执行

常量区

  • 存储字符串字面量、全局常量
  • 特点:只读属性

在这里插入图片描述


Static

static 是一个关键字,通常用来修饰变量或函数。

局部静态变量

  • 生命周期随程序,程序运行期间一直存在
  • 函数调用结束后不会销毁

全局静态变量和函数

  • 具有内部链接属性
  • 链接时其他文件不可见,只能在当前文件中使用
  • 可以避免命名冲突

类静态成员变量

  • 属于整个类,而不是某个类的实例
  • 所有的类对象共享一份静态成员变量

类静态成员函数

  • 属于整个类,而不是某个类的实例
  • 没有 this 指针,因此无法调用普通成员函数
  • 只能调用静态成员变量和静态成员函数

四种智能指针 && 循环引用

智能指针就是在 RAII(资源获取立即初始化) 的思想上设计的类。
通过封装对象指针,自动管理内存的分配和释放,同时提供运算符重载访问对象资源。
可以解决异常的栈展开资源没有析构的安全问题等,避免内存泄露。


auto_ptr(已废弃)

  • C++98 引入的智能指针
  • 独占资源所有权,不支持移动语义
  • 只支持拷贝和赋值转移资源所有权
  • 转移后原来的 auto_ptr 指针会悬空,再次访问会有野指针问题
  • 语义不明确,通过拷贝隐式转移,容易误用
  • 目前已被弃用,不建议使用

unique_ptr

  • 独占资源所有权
  • 明确只有一个智能指针可以管理一个对象
  • 不支持拷贝,赋值重载和拷贝构造都被 =delete 删除了
  • 支持移动语义,通过 move() 来转移资源所有权
  • move() 之后原来的 unique_ptr 也会悬空
  • 语义明确,比 auto_ptr 更安全

shared_ptr

  • 可以共享资源,允许多个智能指针共享同一个对象资源
  • 通过引用计数记录指向对象的 shared_ptr 的数目
  • 只有当引用计数减为 0 才释放资源

原理

shared_ptr 核心在于维护一个控制块,包含:

在这里插入图片描述

创建一个 shared_ptr 对象时,初始化强引用计数为 1。
如果是通过 make_shared 创建的话,还会同时开辟控制块和对象的空间,避免内存碎片,提高性能。

复制一个 shared_ptr,强引用计数 ++
销毁一个 shared_ptr,强引用计数 --
如果强引用计数变为 0,销毁对象;如果弱引用计数不为 0,不释放控制块。
当强引用计数和弱引用计数都为 0 时,才会释放控制块。
因为 weak_ptr 需要通过控制块的强引用计数判断资源是否过期,否则访问资源会出现野指针。


shared_ptr 的线程安全问题

  • shared_ptr 本身并不是完全线程安全的
  • 它的引用计数是线程安全的,因为是通过标准库的 atomic 原子操作保证的
  • 保证多线程对引用增加删除时操作是原子的,所以析构也是线程安全的
  • 当引用计数减为 0 时,调用析构,只会调用一次

但是:

  • 多线程对同一个 shared_ptr 对象进行赋值操作时,涉及指针的修改
  • 会导致数据竞争,导致数据不一致
  • 此时就会出现线程安全问题,可以通过加上互斥锁解决

weak_ptr

  • 弱引用智能指针,指向一个由 std::shared_ptr 管理的对象
  • 不增加引用计数,不参与资源对象的管理
  • 通过 lock() 获取 shared_ptr 使用访问对象
  • 检查控制块的强引用计数判断资源是否过期
  • 如果对象还存在,创建一个新的 shared_ptr 并返回
  • 如果对象不存在,返回一个空的 shared_ptr 表示资源过期
  • 使用 expired() 方法访问控制块的强引用计数,检查对象是否已经被销毁

循环引用

  • 多个 shared_ptr 对象互相指向,持有对方的引用,形成闭环
  • 此时当 shared_ptr 指针释放后,因为互相指向,引用计数依然 > 0
  • 每个对象的释放都依赖于对方,就会陷入循环引用的闭环
  • 导致每个对象都不会释放,引发内存泄露

解决方法:
将其中一个对象的 shared_ptr 替换为 weak_ptr 即可,因为 weak_ptr 不增加引用计数,不参与对象的管理。

RAII

RAII(Resource Acquisition Is Initialization,资源获取即初始化)是一种编程范式,主要用于管理资源的生命周期,确保资源在使用期间始终保持有效,并在不再需要时正确释放。主要用于资源管理、异常安全。

RAII 的核心思想是:
将资源的获取与对象的构造绑定在一起,将资源的释放与对象的析构绑定在一起。


构造函数和析构函数的虚函数问题

构造函数不能是虚函数

虚函数表的构造是在对象构造中完成的,对象构造完成后虚函数表才构造完成。而在调用构造函数时,对象还没构造完成,即虚函数表也没有构造完成,此时就无法在虚函数表中找到构造函数的实现。


析构函数通常是虚函数,否则会导致内存泄漏

基类的指针可以指向派生类对象,此时如果基类的析构函数不是虚函数,指向派生类的基类指针在通过指针删除时,就只会调用基类的析构函数,导致派生类中的分配资源没有得到释放,从而导致内存泄露。

因此,基类的析构函数通常声明为虚函数,并且编译器会把析构函数的名字统一处理为 destructor。派生类完成对基类析构函数的重写后,就可以实现多态析构:

  • 基类调用基类的析构函数
  • 派生类调用派生类的析构函数

这样可以保证资源的正确释放,防止内存泄漏。

后言

这就是【计算机基础理论知识】C++篇(一)。大家自己好好消化!今天就分享到这!感谢各位的耐心垂阅!咱们下期见!拜拜~

http://www.dtcms.com/a/269473.html

相关文章:

  • 暑假读书笔记第四天
  • 【Python-GEE】如何利用Landsat时间序列影像通过调和回归方法提取农作物特征并进行分类
  • python transformers库笔记(BertForTokenClassification类)
  • 【牛客刷题】小红的与运算
  • node.js中yarn、npm、cnpm详解
  • 精益管理与数字化转型的融合:中小制造企业降本增效的双重引擎
  • 算法训练营DAY29 第八章 贪心算法 part02
  • 实战Linux进程状态观察:R、S、D、T、Z状态详解与实验模拟
  • 联通线路物理服务器选择的关键要点
  • No Hack No CTF 2025Web部分个人WP
  • Django双下划线查询
  • 微信小程序控制空调之接收MQTT消息
  • 如何利用AI大模型对已有创意进行评估,打造杀手级的广告创意
  • deepseek实战教程-第九篇开源模型智能体开发框架solon-ai
  • Python爬取知乎评论:多线程与异步爬虫的性能优化
  • React18+TypeScript状态管理最佳实践
  • Jenkins 使用宿主机的Docker
  • 深入解析 structuredClone API:现代JS深拷贝的终极方案
  • Ubuntu 版本号与别名对照表(部分精选)
  • Java使用接口AES进行加密+微信小程序接收解密
  • Linux Ubuntu系统下载
  • Docker企业级应用:从入门到生产环境最佳实践
  • any实现(基于LLVM中libcxx实现分析)
  • 深入理解Java虚拟机(JVM):从内存管理到性能优化
  • 基于Java+Maven+Testng+Selenium+Log4j+Allure+Jenkins搭建一个WebUI自动化框架(1)搭建框架基本雏形
  • C++11标准库算法:深入理解std::find, std::find_if与std::find_if_not
  • iOS Widget 开发-3:Widget 的种类与尺寸(主屏、锁屏、灵动岛)
  • el-button传入icon用法可能会出现的问题
  • Unity开发如何解决iOS闪退问题
  • 数据分析-59-SPC统计过程控制XR图和XS图和IMR图和CPK分析图