C++11 智能指针:unique_ptr、shared_ptr和weak_ptr 功能特性 模拟实现

news2025/2/28 3:23:09

文章目录

  • unique_ptr
    • 功能和特性
    • 使用场景
    • make_unique
    • 模拟实现
  • shared_ptr
    • 功能和特性
    • 使用场景
    • make_shared
    • 模拟实现
  • weak_ptr

C++ 中智能指针都是 RAII(Resource Acquisition Is Initialization)机制的典型应用,在构造时获取资源,在析构时释放资源,将资源管理与对象的生命周期绑定,使得资源管理更加直观和可靠。

unique_ptr

std::unique_ptr 的主要作用是对动态分配的资源进行严格的独占式管理,确保在其生命周期结束时自动释放所管理的资源,从而防止内存泄漏,让资源的生命周期与 std::unique_ptr 变量的生命周期紧密绑定,无需手动调用 delete 操作符,使资源管理更安全、更高效。

功能和特性

  • 独占所有权:同一时刻只能有一个 std::unique_ptr 指向给定的资源,保证了资源的独占访问,避免多个指针同时操作同一资源带来的冲突和数据不一致问题。
  • 移动语义:支持移动构造和移动赋值操作,允许将资源的所有权从一个 std::unique_ptr 转移到另一个 std::unique_ptr,但不支持复制操作,提高了资源转移的效率,避免了不必要的资源复制。
  • 自动释放资源:当 std::unique_ptr 对象超出作用域或被显式销毁时,会自动调用其析构函数来释放所管理的资源,无需手动释放,降低了忘记释放资源导致内存泄漏的风险。
  • 可定制删除器:可以通过模板参数指定自定义的删除器,用于定义释放资源的特定方式,以满足不同资源类型的特殊释放需求。
  • 空指针值:可以通过默认构造函数创建一个空的 std::unique_ptr,表示不管理任何资源,也可以通过将其赋值为 nullptr 来使其变为空指针。

使用场景

  • 函数内部局部资源管理:在函数内部动态分配资源时,使用 std::unique_ptr 来管理这些资源,确保函数执行结束时资源能被正确释放,例如在函数中动态分配数组或对象。
  • 资源独占场景:当某个资源只需要被一个对象独占使用时,如一个文件操作类中使用 std::unique_ptr 来管理文件句柄,保证同一时间只有该对象能访问文件。
  • 作为函数返回值:函数可以返回一个 std::unique_ptr,将动态分配资源的所有权安全地转移给调用者,调用者可以继续管理该资源,而无需担心资源的释放问题。
  • 管理动态分配的数组:可以使用 std::unique_ptr 来管理动态分配的数组,通过指定数组删除器来确保数组内存的正确释放。

make_unique

std::make_unique 是 C++14 引入的一个辅助函数,用于创建并返回 std::unique_ptr 对象。它在动态分配内存时,提供了更简洁和安全的方式,与 std::unique_ptr 的构造配合使用,减少了潜在的错误风险。

特点std::make_uniquestd::unique_ptrnew
内存分配高效,单一操作分配内存初始化和分配是分离的操作
异常安全更安全,避免资源泄漏可能因异常导致内存泄漏
代码简洁性更简洁,无需手动使用 new手动使用 new,代码更冗长
自定义删除器无法直接使用自定义删除器支持自定义删除器

示例

int main() {
    // 使用 unique_ptr 管理一个 vector<int>
    auto vecPtr = std::make_unique<std::vector<int>>();

    vecPtr->push_back(10);
    vecPtr->push_back(20);
    vecPtr->push_back(30);

    std::cout << "Vector elements: ";
    for (const auto& elem : *vecPtr) {
        std::cout << elem << " ";
    }
    std::cout << "\n";

    // vecPtr2 超出作用域时,资源会自动释放
    return 0;
}

模拟实现

template <typename T>
class UniquePtr {
public:
    explicit UniquePtr(T* ptr = nullptr) : ptr_(ptr) {}
    
    UniquePtr(const UniquePtr&) = delete;
    UniquePtr<T>& operator=(const UniquePtr&) = delete;
    
    UniquePtr(UniquePtr&& other) noexcept : ptr_(other.ptr_) {
        other.ptr_ = nullptr;
    }
    
    UniquePtr& operator=(UniquePtr&& other) noexcept {
        if (this != &other) {
            delete ptr_;
            ptr_ = other.ptr_;
            other.ptr_ = nullptr;
        }
        return *this;
    }
    
    ~UniquePtr() { delete ptr_; }
    
    T& operator*() { return *ptr_; }
    T* operator->() { return ptr_; }
    
    // 获取原始指针
    T* get() const { return ptr_; }
    
    // 重置指针
    void reset(T* ptr = nullptr) {
        if (ptr != ptr_) {
            delete ptr_;
            ptr_ = ptr;
        }
    }
    
    // 释放所有权
    T* release() {
        T* tmp = ptr_;
        ptr_ = nullptr;
        return tmp;
    }

private:
    T* ptr_;
};

shared_ptr

std::shared_ptr 的主要作用是实现资源的共享所有权。多个 std::shared_ptr 可以指向同一个对象,通过引用计数机制来跟踪有多少个 std::shared_ptr 共享该对象。当最后一个指向该对象的 std::shared_ptr 被销毁或重置时,对象的内存会被自动释放,从而避免了内存泄漏。

std::shared_ptr 可能会导致循环引用问题,即两个或多个 std::shared_ptr 相互引用,使得引用计数永远不会变为 0,从而导致内存泄漏。为了解决这个问题,可以使用 std::weak_ptr,它是一种弱引用,不会增加引用计数。

功能和特性

  • 引用计数std::shared_ptr 内部维护一个引用计数,记录有多少个 std::shared_ptr 共享同一个对象。每当一个新的 std::shared_ptr 指向该对象时,引用计数加 1;当一个 std::shared_ptr 被销毁或重置时,引用计数减 1。当引用计数变为 0 时,对象的内存会被自动释放。
  • 共享所有权:多个 std::shared_ptr 可以同时拥有同一个对象的所有权,这使得资源可以在多个地方被安全地使用,而不用担心资源过早释放或重复释放的问题。
  • 自动资源管理std::shared_ptr 会在引用计数变为 0 时自动释放所管理的资源,无需手动调用 delete 操作符,提高了代码的安全性和可维护性。
  • 可复制和赋值std::shared_ptr 支持复制构造和赋值操作,复制或赋值操作会增加引用计数,确保资源的共享和正确管理。
  • 自定义删除器:可以通过模板参数指定自定义的删除器,用于定义释放资源的特定方式,以满足不同资源类型的特殊释放需求。

使用场景

  • 多个对象共享资源:当多个对象需要同时访问和使用同一个资源时,使用 std::shared_ptr 可以方便地实现资源的共享。例如,多个线程可能需要访问同一个数据结构,使用 std::shared_ptr 可以确保该数据结构在所有线程都不再使用时才被释放。
  • 实现对象池:在对象池的实现中,std::shared_ptr 可以用于管理对象的生命周期。当对象从对象池中取出时,使用 std::shared_ptr 管理该对象;当对象被放回对象池或不再使用时,std::shared_ptr 会自动释放对象的内存。

make_shared

std::make_shared 是 C++11 中引入的一个用于创建 std::shared_ptr 对象的函数模板。它通过单次内存分配同时创建被管理的对象和控制块(control block),从而提高效率并减少潜在的内存碎片。

特点std::make_sharedshared_ptrnew
内存分配一次分配,控制块和对象共享内存。两次分配,控制块和对象分开存储。
异常安全构造失败无内存泄漏(更安全)。构造失败时可能导致对象泄漏。
代码简洁性简洁,无需显式 new需要显式使用 new,易出错。
自定义删除器不支持自定义删除器。支持自定义删除器(适合特殊资源管理)。
auto sp = std::make_shared<int>(9); // 同时分配对象和控制块

模拟实现

template <typename T>
class SharedPtr {
public:
    explicit SharedPtr(T* ptr = nullptr) 
        : ptr_(ptr), ref_count_(ptr ? new std::atomic<size_t>(1) : nullptr) {}
    
    SharedPtr(const SharedPtr& other) : ptr_(other.ptr_), ref_count_(other.ref_count_) {
        if (ref_count_)
            ++(*ref_count_);
    }
    
    SharedPtr& operator=(const SharedPtr& other) {
        if (this != other) {
            release();
            ptr_ = other.ptr_;
            ref_count_ = other.ref_count_;
            if (ref_count_)
                	ref_count_->fetch_a;
        }
        return *this;
    }
    
    T& operator*() { return *ptr_; }
    T* operator->() { return ptr_; }
    
    ~SharedPtr() { release(); }
    
    T* get() const { return ptr_; }
    
    int use_count() const {
        return ref_count_ ? ref_count_->load() : 0;
    }
    
private:
    void release() {
        if (ref_count_ && ref_count_->fetch_sub(1) == 0) {
            delete ptr_;
            delete ref_count_;
        }
    }
    
    T* ptr_;
    std::atomic<size_t>* ref_count_;
};

weak_ptr

std::weak_ptr 是 C++ 标准库中的一种智能指针,它是为了配合 std::shared_ptr 而引入的,用于解决 std::shared_ptr 可能存在的循环引用。当多个 std::shared_ptr 相互引用形成循环时,它们的引用计数永远不会降为 0,导致对象无法被释放,而 std::weak_ptr 不影响引用计数,可作为一种弱引用解决此问题。

#include <iostream>
#include <memory>

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::weak_ptr<A> a_ptr; // 使用 weak_ptr 避免循环引用
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a;

    // 此时 a 和 b 的引用计数都为 1,不会造成循环引用
    std::cout << "a use_count: " << a.use_count() << "\n"; // 输出 1
    std::cout << "b use_count: " << b.use_count() << "\n"; // 输出 1

    return 0;
}

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

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

相关文章

git merge -s ours ...的使用方法

当我们在自己的feature branch上开发时&#xff0c;并且已经commit&#xff0c;push了好几次 同时develop分支也commit , push了好几次&#xff0c; 如下图所示 这个时候就不能直接将feature branch上的改动 pull request到develop上面&#xff0c;因为develop基线已经不一样了…

数字可调控开关电源设计(论文+源码)

1 设计要求 在本次数字可调控开关电源设计过程中&#xff0c;对关键参数设定如下&#xff1a; &#xff08;1&#xff09;输入电压&#xff1a;DC24-26V,输出电压&#xff1a;12-24&#xff08;可调&#xff09;&#xff1b; &#xff08;2&#xff09;输出电压误差&#xf…

【DeepSeek】【GPT-Academic】:DeepSeek集成到GPT-Academic(官方+第三方)

目录 1 官方deepseek 1.1 拉取学术GPT项目 1.2 安装依赖 1.3 修改配置文件中的DEEPSEEK_API_KEY 2 第三方API 2.1 修改DEEPSEEK_API_KEY 2.2 修改CUSTOM_API_KEY_PATTERM 2.3 地址重定向 2.4 修改模型参数 2.5 成功调用 2.6 尝试添加一个deepseek-r1参数 3 使用千帆…

DeepSeek R1 + 飞书机器人实现AI智能助手

效果 TFChat项目地址 https://github.com/fish2018/TFChat 腾讯大模型知识引擎用的是DeepSeek R1&#xff0c;项目为sanic和redis实现&#xff0c;利用httpx异步处理流式响应&#xff0c;同时使用buffer来避免频繁调用飞书接口更新卡片的网络耗时。为了进一步减少网络IO消耗&…

Android移动应用开发实践-1-下载安装和简单使用Android Studio 3.5.2版本(频频出错)

一、下载安装 1.Android Studio3.5.2下载地址&#xff1a;Android Studio3.5.2下载地址 其他版本下载地址&#xff1a;其他版本下载地址 2.安装教程&#xff08;可以多找几个看看&#xff09; 安装 | 手把手教你Android studio 3.5.2安装&#xff08;安装教程&#xff09;_a…

Rk3568驱动开发_驱动编写和挂载_2

1.字符驱动介绍&#xff1a; 字符驱动&#xff1a;按照字节流镜像读写操作的设备&#xff0c;读写数据分先后顺序&#xff0c;例如&#xff1a;点灯、按键、IIC、SPI、等等都是字符设备&#xff0c;这些设备的驱动叫字符驱动设备 Linux应用层如何调用驱动&#xff1a; 字符设…

【苍穹外卖】问题笔记

【DAY1 】 1.VCS找不到 好吧&#xff0c;发现没安git 接着发现安全模式有问题&#xff0c;点开代码信任此项目 2.导入初始文件&#xff0c;全员爆红 好像没maven&#xff0c;配一个 并在设置里设置好maven 3.启用注解&#xff0c;见新手苍穹 pom.xml改lombok版本为1.1…

1.1部署es:9200

安装es&#xff1a;root用户&#xff1a; 1.布署java环境 - 所有节点 wget https://d6.injdk.cn/oraclejdk/8/jdk-8u341-linux-x64.rpm yum localinstall jdk-8u341-linux-x64.rpm -y java -version 2.下载安装elasticsearch - 所有节点 wget ftp://10.3.148.254/Note/Elk/…

上传securecmd失败

上传securecmd失败 问题描述&#xff1a;KES V8R6部署工具中&#xff0c;节点管理里新建节点下一步提示上传securecmd失败&#xff0c;如下&#xff1a; 解决办法&#xff1a; [rootlocalhost ~]# yum install -y unzip 上传的过程中会解压&#xff0c;如果未安装unzip依赖包…

C++:dfs,bfs各两则

1.木棒 167. 木棒 - AcWing题库 乔治拿来一组等长的木棒&#xff0c;将它们随机地砍断&#xff0c;使得每一节木棍的长度都不超过 5050 个长度单位。 然后他又想把这些木棍恢复到为裁截前的状态&#xff0c;但忘记了初始时有多少木棒以及木棒的初始长度。 请你设计一个程序…

P9420 [蓝桥杯 2023 国 B] 子 2023

P9420 [蓝桥杯 2023 国 B] 子 2023 题目 分析代码 题目 分析 刚拿到这道题&#xff0c;我大脑简单算了一下&#xff0c;这个值太大了&#xff0c;直观感觉就很难&#xff01;&#xff01; 但是&#xff0c;你仔仔细细的一看&#xff0c;先从最简单的第一步入手&#xff0c;再…

2025-02-26 学习记录--C/C++-C语言 判断字符串S2是否在字符串S1中

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; C语言 判断字符串S2是否在字符串S1中 #include <stdio.h> // 引入标准输入输出库&#xff0c;用于使用 printf 等函数 #…

游戏引擎学习第124天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾/复习 今天是继续完善和调试多线程的任务队列。之前的几天&#xff0c;我们已经介绍了多线程的一些基础知识&#xff0c;包括如何创建工作队列以及如何在线程中处理任务。今天&#xff0c;重点是解决那些我们之前没有注意到…

组件的组成和组件的嵌套关系

组件的组成 首先建一个.vue文件&#xff0c;在里面写一个内容&#xff1a; <template> <div><div class"container">{{ message }}</div> </div> </template> <script> export default{data(){return{message:"组件…

2025 PHP授权系统网站源码

2025 PHP授权系统网站源码 安装教程&#xff1a; PHP7.0以上 先上传源码到服务器&#xff0c;然后再配置伪静态&#xff0c; 访问域名根据操作完成安装&#xff0c; 然后配置伪静态规则。 Ngix伪静态规则&#xff1a; location / { if (!-e $request_filename) { rewrite …

KIMI K1.5:大规模强化学习在大语言模型中的应用与工程实践

目录 1、核心技术创新:长上下文强化学习 2、策略优化的技术细节 2.1、在线镜像下降变体 2.2、长度惩罚机制 2.3、智能采样策略 3、工程架构创新 3.1、混合部署框架 3.2、代码沙箱与奖励模型 3.3、分布式系统架构 4、实验成果与性能提升 5、结论与未来展望 大语言模…

Linux MySQL 8.0.29 忽略表名大小写配置

Linux MySQL 8.0.29 忽略表名大小写配置 问题背景解决方案遇到的问题&#xff1a; 问题背景 突然发现有个大写的表报不存在。 在Windows上&#xff0c;MySQL是默认支持忽略大小写的。 这个时候你要查询一下是不是没有配置&#xff1a; SHOW VARIABLES LIKE lower_case_table…

财务运营域——营收稽核系统设计

摘要 本文主要介绍了营收稽核系统的背景、特点与作用。营收稽核系统的产生源于营收管理复杂性、财务合规与审计需求、提升数据透明度与决策效率、防范舞弊与风险管理、技术进步与自动化需求、多元化业务模式以及跨部门协作与数据整合等多方面因素。其特点包括自动化与智能化、…

30 分钟从零开始入门 CSS

HTML CSS JS 30分钟从零开始入门拿下 HTML_html教程-CSDN博客 30 分钟从零开始入门 CSS-CSDN博客 JavaScript 指南&#xff1a;从入门到实战开发-CSDN博客 前言 最近也是在复习&#xff0c;把之前没写的博客补起来&#xff0c;之前给大家介绍了 html&#xff0c;现在是 CSS 咯…

threejs:document.createElement创建标签后css设置失效

vue3threejs&#xff0c;做一个给模型批量CSS2D标签的案例&#xff0c;在导入模型的js文件里&#xff0c;跟着课程写的代码如下&#xff1a; import * as THREE from three; // 引入gltf模型加载库GLTFLoader.js import { GLTFLoader } from three/addons/loaders/GLTFLoader.…