C/C++语言常见问题-智能指针、多态原理

news2025/4/19 14:59:19

文章目录

  • 智能指针实现原理
  • 智能指针,里面的计数器何时会改变
      • `std::shared_ptr`的引用计数器:
      • `std::weak_ptr`的弱引用计数器:
      • `std::unique_ptr`的控制块:
      • 总结:
  • 智能指针和管理的对象分别在哪个区
  • 面向对象的特性:多态原理
      • 多态性的定义
      • 多态性的类型
      • 多态性的原理
      • 多态性的实现步骤
      • 示例代码(C++)

智能指针实现原理

在C++中,智能指针是一种自动管理内存分配和释放的工具,它们帮助程序员避免内存泄漏和其他与动态内存分配相关的问题。C++11标准引入了几种智能指针,主要包括std::unique_ptrstd::shared_ptrstd::weak_ptr。下面将分别介绍这些智能指针的实现原理。

  1. std::unique_ptr
    std::unique_ptr是一种独占所有权的智能指针,意味着同一时间只能有一个std::unique_ptr指向特定对象。它不允许复制,但可以移动。

实现原理:

  • 构造函数:接受原始指针,并在构造时接管所有权。
  • 析构函数:当std::unique_ptr对象被销毁时,它会自动删除它所拥有的对象。
  • 移动语义:通过移动构造函数和移动赋值运算符,可以将所有权从一个std::unique_ptr转移到另一个,原指针变为空。
  • 删除复制语义:复制构造函数和复制赋值运算符被删除,防止多个指针指向同一对象。
  1. std::shared_ptr
    std::shared_ptr是一种共享所有权的智能指针,允许多个std::shared_ptr实例共享同一对象。

实现原理:

  • 引用计数std::shared_ptr内部维护一个引用计数器,用来跟踪有多少个std::shared_ptr实例共享同一个对象。
  • 构造函数:接受原始指针,并在构造时接管所有权,同时初始化引用计数为1。
  • 拷贝构造函数和拷贝赋值运算符:当std::shared_ptr被复制时,引用计数增加。
  • 移动构造函数和移动赋值运算符:移动语义允许将所有权从一个std::shared_ptr转移到另一个,同时减少原指针的引用计数。
  • 析构函数:当最后一个std::shared_ptr被销毁或被重新赋值时,引用计数减至0,此时自动删除所管理的对象。
  1. std::weak_ptr
    std::weak_ptr是一种不控制对象生命周期的智能指针,通常与std::shared_ptr配合使用,用于解决强引用循环问题。

实现原理:

  • 弱引用计数std::weak_ptr不增加对象的引用计数,但可以观察引用计数。
  • 构造函数:可以从一个std::shared_ptr构造,但不增加引用计数。
  • 析构函数:不删除对象,只是减少内部的弱引用计数。
  • 升级为std::shared_ptr:如果对象仍然存在(即std::shared_ptr的引用计数大于0),std::weak_ptr可以被升级为一个std::shared_ptr

总结
智能指针通过封装原始指针和自动管理内存的生命周期,减少了内存泄漏的风险。std::unique_ptr适用于单一所有权的情况,std::shared_ptr适用于需要多个指针共享同一对象的情况,而std::weak_ptr则用于打破强引用循环,提供更灵活的控制。

智能指针在C++中的底层实现通常依赖于几种基本的数据结构,这些数据结构帮助智能指针管理对象的生命周期和引用计数。以下是这些数据结构的简要说明:

  1. std::unique_ptr
    std::unique_ptr通常不需要复杂的数据结构来管理引用计数,因为它不允许共享所有权。它的实现相对简单,通常只包含一个指向管理对象的原始指针,可能还包括一个自定义删除器(如果提供了的话)。
  2. std::shared_ptr
    std::shared_ptr的实现依赖于两个主要的数据结构:
  • 控制块(Control Block):这是一个结构体,通常包含至少两个指针,一个指向管理的对象,另一个指向一个引用计数器。引用计数器记录了当前有多少个std::shared_ptr实例共享这个对象。
  • 引用计数器:这是一个整数值,用来跟踪有多少个std::shared_ptr实例共享同一个对象。当最后一个std::shared_ptr被销毁或被重新赋值时,引用计数减至0,对象会被自动删除。
  1. std::weak_ptr
    std::weak_ptr的实现也依赖于控制块,但它不直接管理对象的生命周期。它包含以下元素:
  • 弱引用计数器:与std::shared_ptr的控制块中的引用计数器不同,std::weak_ptr的弱引用计数器不增加对象的引用计数,它只是用来跟踪有多少个std::weak_ptr实例观察这个对象。
  • 指向控制块的指针std::weak_ptr包含一个指向对象控制块的指针,通过这个指针,std::weak_ptr可以访问对象的引用计数和对象本身。

实现细节
在实际的实现中,控制块可能包含更多的信息,例如:

  • 自定义删除器:如果提供了自定义删除器,控制块中也会包含指向这个删除器的指针。
  • 线程安全:在多线程环境中,引用计数的增加和减少需要是原子操作,以避免竞态条件。

总结
智能指针的底层实现通过控制块和引用计数器来管理对象的生命周期和共享状态。这些数据结构的设计使得智能指针能够在不牺牲性能的情况下,提供自动的内存管理和引用计数功能。

智能指针,里面的计数器何时会改变

在C++中,智能指针的引用计数器(对于std::shared_ptr)或控制块(对于std::unique_ptrstd::weak_ptr)的值会在以下情况下改变:

std::shared_ptr的引用计数器:

  1. 增加计数

    • 当通过拷贝构造函数或拷贝赋值运算符创建一个新的std::shared_ptr实例时,指向同一对象的std::shared_ptr数量增加,引用计数器增加。
    • 当通过std::shared_ptrclone函数创建一个新的std::shared_ptr时,引用计数器也会增加。
  2. 减少计数

    • 当一个std::shared_ptr实例被销毁(例如离开其作用域或被显式析构)时,引用计数器减少。
    • 当通过移动构造函数或移动赋值运算符将std::shared_ptr的所有权转移给另一个实例时,原实例的引用计数器减少。
    • 当通过std::shared_ptrreset方法重置指针时,如果新的控制块与旧的不同,旧控制块的引用计数器减少。

std::weak_ptr的弱引用计数器:

  1. 增加计数

    • 当创建一个新的std::weak_ptr实例,并且它与一个现有的std::shared_ptr共享同一个控制块时,弱引用计数器增加。
  2. 减少计数

    • 当一个std::weak_ptr实例被销毁时,如果它之前是指向一个对象的最后一个弱引用,弱引用计数器减少。

std::unique_ptr的控制块:

std::unique_ptr不使用引用计数器,因为它不允许共享所有权。但是,它的控制块(如果提供了自定义删除器)会在以下情况下改变:

  1. 所有权转移
    • 当通过移动构造函数或移动赋值运算符将std::unique_ptr的所有权从一个实例转移到另一个实例时,原实例的控制块变为空。

总结:

  • std::shared_ptr:引用计数器会在拷贝、赋值、销毁、移动和重置时改变。
  • std::weak_ptr:弱引用计数器会在创建和销毁时改变,但不会直接影响对象的生命周期。
  • std::unique_ptr:不使用引用计数器,控制块在所有权转移时改变。

智能指针的这些机制确保了对象的生命周期被正确管理,同时避免了内存泄漏和其他资源管理错误。

智能指针和管理的对象分别在哪个区

(智能指针本身在栈区,托管的资源在堆区,利用了栈对象超出生命周期后自动析构的特征,所以无需手动delete释放资源)
在C++中,智能指针和它们管理的对象的存储位置取决于它们是如何被创建和使用的。以下是智能指针和它们管理的对象可能位于的内存区域:

  1. 智能指针本身

    • 栈区(Stack):如果智能指针是局部变量或者作为函数参数传递,它们通常存储在栈区。当函数结束时,智能指针会被自动销毁,它们的析构函数会被调用,从而管理的对象也会被适当地释放。
    • 堆区(Heap):如果智能指针是通过new操作符或者std::make_shared等函数动态分配的,它们将存储在堆区。这种情况下,需要手动调用delete来释放智能指针本身占用的内存,但它们管理的对象会在智能指针被销毁时自动释放。
  2. 管理的对象

    • 堆区(Heap):智能指针管理的对象通常存储在堆区。这是因为对象的生命周期可能需要比创建它们的函数更长,或者对象的大小可能在编译时未知,因此不能在栈区分配。
    • 例如,std::shared_ptrstd::unique_ptr通常指向堆区中的对象。当智能指针被销毁时,它们会自动释放指向的对象。
  3. 控制块

    • 对于std::shared_ptr来说,还有一个与之关联的控制块,它也存储在堆区。控制块包含了引用计数器,用于跟踪有多少个std::shared_ptr实例共享同一个对象。当最后一个引用被销毁时,控制块中的引用计数器会减少到0,对象和控制块都会被释放。
  4. 弱引用计数器

    • 对于std::weak_ptr来说,它不直接管理对象的生命周期,但是它可能包含一个指向控制块的指针,这个指针用于跟踪弱引用的数量。这个计数器通常也存储在控制块中,与std::shared_ptr的控制块相同。

总结来说,智能指针本身可以位于栈区或堆区,而它们管理的对象通常位于堆区。智能指针的析构函数确保了对象在不再需要时被自动释放,这是通过控制块中的引用计数器来管理的。这种设计模式减少了手动管理内存的需要,从而降低了内存泄漏和其他资源管理错误的风险。

面向对象的特性:多态原理

面向对象编程(OOP)是一种编程范式,它使用“对象”来设计应用程序和程序结构。在面向对象编程中,多态性(Polymorphism)是一个核心特性,它允许你以统一的方式处理不同类型的对象。多态性使得程序更加灵活和可扩展。

多态性的定义

多态性指的是同一个操作作用于不同的对象时,可以有不同的解释和不同的执行结果。在编程中,这通常意味着同一个方法或函数可以被不同的对象以不同的方式实现。

多态性的类型

  1. 编译时多态(静态多态)

    • 方法重载(Overloading):同一个类中有多个同名方法,它们的参数列表不同(参数类型、数量或顺序不同)。
    • 操作符重载(Operator Overloading):对已有的操作符赋予新的功能,使其可以作用于用户定义的类型。
  2. 运行时多态(动态多态)

    • 方法重写(Overriding):在派生类中重新定义基类的方法。这是通过使用虚函数(virtual function)实现的,允许在运行时根据对象的实际类型调用相应的方法。

多态性的原理

  • 虚函数表(Virtual Table):在C++中,运行时多态性是通过虚函数表实现的。每个包含虚函数的类都有一个虚函数表,这个表包含了指向类中所有虚函数的指针。
  • 虚函数指针:每个对象都有一个指向其类虚函数表的指针。当通过基类指针或引用调用虚函数时,会使用这个指针来查找并调用正确的函数。

多态性的实现步骤

  1. 声明虚函数:在基类中声明函数为虚函数(使用virtual关键字)。
  2. 派生类重写:在派生类中重写这个虚函数。
  3. 使用基类指针或引用:通过基类的指针或引用来调用虚函数。

示例代码(C++)

#include <iostream>

class Animal {
public:
    virtual void speak() {
        std::cout << "Animal makes a sound\n";
    }
    virtual ~Animal() {} // 虚析构函数以确保派生类对象的正确析构
};

class Dog : public Animal {
public:
    void speak() override {
        std::cout << "Dog barks\n";
    }
};

class Cat : public Animal {
public:
    void speak() override {
        std::cout << "Cat meows\n";
    }
};

int main() {
    Animal* myAnimal = new Dog();
    myAnimal->speak(); // 输出 "Dog barks"

    Animal* anotherAnimal = new Cat();
    anotherAnimal->speak(); // 输出 "Cat meows"

    delete myAnimal;
    delete anotherAnimal;
    return 0;
}

在这个例子中,Animal类有一个虚函数speak()DogCat类分别重写了这个函数。通过基类指针调用speak()时,会根据对象的实际类型调用相应的函数,这就是多态性的体现。

多态性是面向对象编程中非常重要的特性,它提高了代码的可重用性和可维护性,同时也使得代码更加灵活和易于扩展。

C++11 是 C++ 语言的一次重大更新,它引入了许多新特性,使得 C++ 编程更加现代化和高效。以下是你提到的几个关键特性的详细介绍:

  1. 智能指针(Smart Pointers)

    • std::unique_ptr:表示独占所有权的智能指针,确保同一时间只有一个智能指针可以指向某个对象。当 std::unique_ptr 被销毁时,它所拥有的对象也会被自动删除。
    • std::shared_ptr:表示共享所有权的智能指针,多个 std::shared_ptr 可以指向同一个对象,对象会在最后一个指向它的智能指针被销毁时自动删除。
    • std::weak_ptr:用于解决 std::shared_ptr 相互引用时可能出现的循环引用问题。std::weak_ptr 不会增加对象的引用计数,它可以用来观察一个对象而不增加其引用计数。
  2. 类型推导(Type Deduction)

    • auto 关键字:允许编译器自动推导变量的类型。例如,auto x = someFunction();x 的类型将与 someFunction() 的返回类型相同。
    • decltype:用于推导表达式的类型,但不创建一个值。例如,decltype(someVariable) 将推导出 someVariable 的类型。
  3. Lambda 表达式

    • Lambda 表达式是一种匿名函数,可以在需要的地方定义并立即使用。它们通常用于简短的函数对象,或者作为参数传递给函数。
    • 基本语法:[capture](parameters) -> return_type { function_body }
    • capture 可以是值捕获(如 x)或引用捕获(如 &x),也可以是 this 指针的捕获。
    • parameters 是 Lambda 表达式的参数列表,如果省略参数列表,则默认为 ()
    • return_type 是 Lambda 表达式的返回类型,如果省略,则由编译器根据 function_body 推导。
  4. 多种容器的使用

    • std::array:固定大小的数组,大小在编译时确定。
    • std::vector:动态大小的数组,可以自动调整大小。
    • std::deque:双端队列,支持快速的随机访问和在两端快速插入/删除。
    • std::list:双向链表,支持快速的插入和删除操作。
    • std::forward_list:单向链表,只支持在头部和尾部快速插入/删除。
    • std::mapstd::unordered_map:键值对的集合,分别基于红黑树和哈希表实现。
    • std::setstd::unordered_set:元素集合,分别基于红黑树和哈希表实现。
  5. 多线程的使用

    • std::thread:表示一个线程,可以用来创建和管理线程。
    • std::mutex:互斥锁,用于保护共享数据,防止多个线程同时访问。
    • std::lock_guardstd::unique_lock:RAII(资源获取即初始化)风格的锁,用于自动管理互斥锁的锁定和解锁。
    • std::condition_variable:条件变量,用于线程间的同步,允许一个或多个线程等待某个条件成立。
    • std::futurestd::promise:用于线程间的数据传递,std::promise 可以设置一个值,而 std::future 可以获取这个值。

这些特性极大地增强了 C++ 的表达力和灵活性,使得编写现代、高效、安全的代码变得更加容易。

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

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

相关文章

Python 实现日志备份守护进程

实训背景 假设你是一名运维工程师&#xff0c;需要为公司的监控系统开发一个简单的日志备份守护进程。该进程需满足以下需求&#xff1a; 后台运行&#xff1a;脱离终端&#xff0c;长期监控指定目录&#xff08;如 /var/log/app/&#xff09;中的日志文件。自动备份&#xf…

Electricity Market Optimization 探索系列(VII)- 直流潮流方程的推导及例题

本文参考书籍&#xff1a;电力经济与电力市场&#xff0c;甘德强&#xff0c;杨莉&#xff0c;冯冬涵 著 link \hspace{1.6em} 文章的结构如下&#xff1a;围绕电力传输系统中短线路的等值等效模型&#xff0c;从节点注入功率的角度和线路功率的角度分析电网中的潮流&#xff0…

路由过滤实验

实验拓扑以及要求 此实验总结 1.ip-prefix 拒绝192.168.4.1 32,这样写的话并不会匹配192.168.4.1 32,需要加上范围less-eq 32,也就是说,192.168.4.1 32只是规则的范围,匹配还是得写范围 2.router-policy适合用在边界路由器引入 filter-policy都可以用 配置IP 配置ospf,rip …

Idea连接远程云服务器上的MySQL,开放云服务器端口

1.开放云服务器的3306端口 &#xff08;1&#xff09;进入到云服务器的控制台 &#xff08;2&#xff09;点击使用的云服务器 &#xff08;3&#xff09;点击 配置安全组规则 &#xff08;4&#xff09;添加规则 &#xff08;5&#xff09;开放端口 2.创建可以远程访问…

Oracle查询大表的全部数据

2000w的大表 表结构如下&#xff0c;其中id是索引 查询处理慢的写法 List<String> queryLoidForPage(Integer startNum,Integer endNum){try {Connection oracleConnection initBean.oracleConnection;Statement stmt oracleConnection.createStatement();// 4.执行查…

PyTorch生成式人工智能实战(1)——神经网络与模型训练过程详解

PyTorch生成式人工智能实战&#xff08;1&#xff09;——神经网络与模型训练过程详解 0. 前言1. 传统机器学习与人工智能2. 人工神经网络基础2.1 人工神经网络组成2.2 神经网络的训练 3. 前向传播3.1 计算隐藏层值3.2 执行非线性激活3.3 计算输出层值3.4 计算损失值3.5 实现前…

基于X86/Nvidia+FPGA大模型具身智能机器人控制器解决方案,同时拥有算力与实时的便利

2025年成为人形机器人产业化元年&#xff0c;行业已突破早期实验室研发阶段&#xff0c;进入"场景验证量产爬坡"新周期&#xff0c;预计2031年具身智能市场规模有望突破万亿元。这一进程的背后&#xff0c;是硬件算力、实时控制、环境适应等底层技术的系统性突破——…

使用 OpenRewrite 简化 Java 和 SpringBoot 迁移

大家好&#xff0c;这里是架构资源栈&#xff01;点击上方关注&#xff0c;添加“星标”&#xff0c;一起学习大厂前沿架构&#xff01; 移民的挑战 随着 Spring Boot 2.x 等旧版本即将到期且不再获得支持&#xff0c;迁移到较新版本对于安全性、兼容性和性能改进至关重要。但…

2025中国移动云智算大会回顾:云智变革,AI+跃迁

4月10日&#xff0c;2025中国移动云智算大会在苏州举办。会上&#xff0c;中国移动开启“由云向智”新范式&#xff0c;以“智”为核心开辟算网新生态&#xff0c;彰显其在AI新时代的战略远见与技术引领力。 “云智算”将如何通过算网基础设施与人工智能核心技术的深度融合&am…

Unity URP Moblie AR示例工程,真机打包出来,没阴影

效果&#xff1a; unity ar示例演示 现象&#xff1a; 真机打包测试私活没有阴影 Unity版本&#xff1a;2022.3.4f1c1 分析原因&#xff1a; Prefab &#xff1a;ARFeatheredPlane中也有材质&#xff0c;一个用于环境遮挡&#xff0c;一个用于阴影接受。 按理说有啊。 urp …

【AI】——结合Ollama、Open WebUI和Docker本地部署可视化AI大语言模型

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大三学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

在 MoonBit 中引入 Elm 架构:用简单原则打造健壮的 Web 应用

Elm 是一种纯函数式编程语言&#xff0c;专为构建前端 Web 应用程序而设计。它编译为 JavaScript&#xff0c;强调简洁性、性能和健壮性。 纯函数式的含义是函数没有副作用&#xff0c;这使得代码更易于理解和调试。通过强大的静态类型检查&#xff0c;Elm 确保应用程序不会抛…

PDF 转换为 Word、HTML、LaTeX 和 Markdown 格式

PDF 转换为 Word、HTML、LaTeX 和 Markdown 格式 1. Doc2XReferences https://doc2x.com/ 1. Doc2X References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/ [2] GPT 学术优化 (GPT Academic), https://github.com/binary-husky/gpt_academic [3] 学术版 GPT 网页…

华为OD机试真题——统计匹配的二元组个数(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 2025华为OD真题目录全流程解析/备考攻略/经验分享 华为OD机试真题《统计匹配…

MySQL表的增删改查进阶版

Mysql 1、数据库的约束1.1约束类型1.2 NULL约束1.3 UNIQUE&#xff1a;唯一约束1.4 DEFAULT&#xff1a;默认值约束1.5 PRIMARY KEY&#xff1a;主键约束&#xff08;重点&#xff09;1.6 FOREIGN KEY&#xff1a;外键约束&#xff08;重点&#xff09; 2.表的设计2.1一对一2.2…

记录 | Pycharm中如何调用Anaconda的虚拟环境

目录 前言一、步骤Step1 查看anaconda 环境名Step2 Python项目编译器更改 更新时间 前言 参考文章&#xff1a; 参考视频&#xff1a;如何在pycharm中使用Anaconda创建的python环境 自己的感想 这里使用的Pycharm 2024专业版的。我所使用的Pycharm专业版位置&#xff1a;【仅用…

qt pyqt5的开发, 修改psd图像

这是引子, 需要将这个 photoshop-python-api 进行使用 https://juejin.cn/post/7445112318693621797#heading-4 这个是ps-python-api的官网, 在里面找api文档 https://pypi.org/project/photoshop-python-api/ 源码.gitee.url https://gitee.com/lbnb/psd_work.git 一. 安装必要…

Spring 中的循环依赖问题:解决方案与三级缓存机制

目录 Spring 中的循环依赖问题&#xff1a;解决方案与三级缓存机制什么是循环依赖&#xff1f;循环依赖的定义循环依赖的举例 Spring 中的循环依赖类型1. 构造器注入引发的循环依赖2. Setter 注入引发的循环依赖3. 字段注入&#xff08;Autowired&#xff09;引发的循环依赖 Sp…

ios接入穿山甲【Swift】

1.可接入的广告&#xff0c;点击右下角查看接入文档 https://www.csjplatform.com/union/media/union/download/groMore 2.进入接入文档&#xff0c;选择最新版本进行接入 pod Ads-CN-Beta,6.8.0.2pod GMGdtAdapter-Beta, 4.15.22.0pod GDTMobSDK,4.15.30pod KSAdSDK,3.3.74.0p…

电脑一直不关机会怎么样?电脑长时间不关机的影响

现代生活中&#xff0c;许多人会让自己的电脑24小时不间断运行&#xff0c;无论是为了持续的工作、娱乐&#xff0c;还是出于忘记关机的习惯。然而&#xff0c;电脑长时间不关机&#xff0c;除了提供便利之外&#xff0c;也可能对设备的健康产生一系列影响。本文将为大家介绍电…