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

59 C++ 现代C++编程艺术8-智能指针

C++ 现代C++编程艺术8-智能指针

文章目录

  • C++ 现代C++编程艺术8-智能指针
    • 一 智能指针原理
    • 二、`unique_ptr`独占指针(独占所有权)
    • 三、`shared_ptr`共享指针(共享所有权)
    • 四、`weak_ptr`弱引用指针(观察者指针)
    • 五、 std::make_shared内存分配
    • 六、 智能指针的高级用法
    • 七、 选择智能指针的准则

一 智能指针原理

裸指针: 原生指针,int *p = new int(10)

  • 提前释放和忘记释放都可造成错误。

智能指针: 用类模板对指针进行自动管理。

  1. 智能指针原理
    用简单的示例来说明智能指针的原理

    #include <iostream>template<typename Ty>
    class Auto_Ptr {
    public://不能进行赋值,还能通过构造函数来初始化explicit Auto_Ptr(Ty* ptr):ptr(ptr){}// 禁止拷贝构造和赋值Auto_Ptr(const Auto_Ptr&) = delete;Auto_Ptr& operator=(const Auto_Ptr&) = delete;~Auto_Ptr(){if(nullptr != ptr){delete ptr;ptr = nullptr;    // 避免悬空指针 std::cout<<"指针已释放!"<<std::endl;}}//指针取值操作Ty& operator*(){return *ptr;}//->成员访问指针操作符Ty* operator->(){return ptr;}private:Ty* ptr;
    };// 定义一个测试类,用于演示->操作符
    class TestClass {
    public:int value;std::string name;void printInfo() {std::cout << "Name: " << name << ", Value: " << value << std::endl;}
    };int main() {Auto_Ptr<int> dat(new int(10));std::cout<<*dat<<std::endl;//输出: 10 指针已释放!// 类类型使用示例 Auto_Ptr<TestClass> objPtr(new TestClass());// 使用->操作符访问成员变量 objPtr->value = 42;objPtr->name = "智能指针测试";// 使用->操作符调用成员函数 objPtr->printInfo(); //输出: Name: 智能指针测试, Value: 42// 也可以这样使用,但不太常见(*objPtr).value = 100;(*objPtr).printInfo();//输出: Name: 智能指针测试, Value: 100// 指针已释放return 0;
    }
    

二、unique_ptr独占指针(独占所有权)

▶ 特点:

  • 唯一所有权:同一时间只能有一个unique_ptr指向资源,独占所有权,不能复制,只能移动

  • 零额外开销:无引用计数机制,性能接近裸指针

  • 支持自定义删除器(如管理文件句柄、网络连接等)

  • 适用于资源独占场景
    ▶ 示例代码:

    #include <iostream>
    #include <memory>class TestClass {
    public:int value;std::string name;void printInfo() {std::cout << "Name: " << name << ", Value: " << value << std::endl;}
    };int main() {auto ptr = std::make_unique<int>(42);  // C++14推荐创建方式 std::unique_ptr<TestClass> obj(new TestClass());// 使用指针 obj->printInfo();(*obj).printInfo();// 所有权转移 auto newOwner = std::move(ptr);  // 原ptr变为nullptr // 管理数组 auto arr = std::make_unique<int[]>(5);arr[0] = 10;// 自定义删除器(管理文件)std::unique_ptr<FILE, decltype(&fclose)> filePtr(fopen("data.txt", "r"), fclose);// 自动释放内存 
    return 0;
    }
    

三、shared_ptr共享指针(共享所有权)

▶ 特点:

  • 引用计数:多个指针共享资源(多个指针可以共享同一对象),计数归零时自动释放

  • 支持弱引用:需配合weak_ptr打破循环引用

  • 控制块存储计数:推荐用make_shared合并内存分配
    ▶ 示例代码

    #include <iostream>
    #include <memory>class TestClass {
    public:int value;std::string name;void printInfo() {std::cout << "Name: " << name << ", Value: " << value << std::endl;}
    };int main() {auto sp1 = std::make_shared<TestClass>();std::shared_ptr<TestClass> sp2 = sp1;  // 引用计数+1 std::cout << "引用计数: " << sp1.use_count()  << std::endl;//引用计数: 2std::shared_ptr<TestClass> sp3 = sp1;  // 引用计数+1 std::cout << "引用计数: " << sp1.use_count()  << std::endl;//引用计数: 3{std::shared_ptr<TestClass> sp4 = sp1;  // 引用计数+1 std::cout << "引用计数: " << sp1.use_count()  << std::endl;//引用计数: 4}//sp4 离开作用域,引用计数减少 std::cout << "引用计数: " << sp1.use_count()  << std::endl;//引用计数: 3// 循环引用问题(错误示例)struct Node {std::shared_ptr<Node> next;  // 相互持有导致内存泄漏 };auto nodeA = std::make_shared<Node>();auto nodeB = std::make_shared<Node>();nodeA->next = nodeB;nodeB->next = nodeA;// 使用weak_ptr打破循环 struct SafeNode {std::weak_ptr<SafeNode> next;  // 弱引用不增加计数 };// 最后一个shared_ptr销毁时释放对象 
    return 0;
    }
    

四、weak_ptr弱引用指针(观察者指针)

▶ 特点

  • 不拥有所有权:不影响引用计数
  • 必须转换为shared_ptr才能访问对象
  • 提供expired()方法检测资源有效性
  • 用于解决shared_ptr的循环引用问题
    ▶ 示例代码
#include <iostream>
#include <memory>
#include <vector>class TestClass {
public:int value;std::string name;void printInfo() {std::cout << "Name: " << name << ", Value: " << value << std::endl;}
};int main() {auto shared = std::make_shared<int>(100);std::weak_ptr<int> weak = shared;if (!weak.expired()) {if (auto temp = weak.lock()) {  // 转换为shared_ptr *temp = 200;  // 安全访问 }}using Resource = TestClass;// 典型应用:对象池模式 class ObjectPool {std::vector<std::weak_ptr<Resource>> pool;public:std::shared_ptr<Resource> acquire() {for (auto& wp : pool) {if (auto sp = wp.lock()) return sp;}return std::shared_ptr<Resource>(new Resource);}};// 最后一个shared_ptr销毁时释放对象 
return 0;
}

五、 std::make_shared内存分配

基本用法
auto ptr = std::make_shared<Type>(args...);

  • Type 是要创建的对象类型
  • args… 是传递给 Type 构造函数的参数
  1. 使用示例

make_shared通常比直接使用 shared_ptr 构造函数更高效,因为:

  • 它只需要一次内存分配(传统方式需要两次)

  • 它将对象和控制块(包含引用计数等)分配在连续内存中

    //传统方式:
    std::shared_ptr<Widget> spw(new Widget);  // 两次分配:对象和控制块//make_shared 方式:
    auto spw = std::make_shared<Widget>();    // 一次分配 
    
  • 基本类型用法

    auto intPtr = std::make_shared<int>(42);
    std::cout << *intPtr << std::endl;  // 输出: 42
    
  • 类对象用法

    class MyClass {
    public:MyClass(int x, std::string s) : x(x), s(s) {}void print() { std::cout << s << ": " << x << std::endl; }
    private:int x;std::string s;
    };auto objPtr = std::make_shared<MyClass>(10, "value");
    objPtr->print();  // 输出: value: 10
    

六、 智能指针的高级用法

  1. 自定义删除器

    #include <memory>
    #include <iostream>
    #include <cstdio>void fileDeleter(FILE* file) {if (file) {fclose(file);std::cout << "File closed\n";}
    }int main() {// 使用自定义删除器管理文件 std::unique_ptr<FILE, decltype(&fileDeleter)> filePtr(fopen("test.txt",  "r"), fileDeleter);if (filePtr) {char buffer[100];fgets(buffer, 100, filePtr.get()); std::cout << "Read: " << buffer;}// 文件自动关闭return 0;
    }
    
  2. 数组支持

    #include <memory>
    #include <iostream>int main() {// C++11方式 std::unique_ptr<int[]> arr1(new int[5]);// C++14推荐方式 auto arr2 = std::make_unique<int[]>(5);for (int i = 0; i < 5; ++i) {arr1[i] = i * 2;arr2[i] = i * 3;}for (int i = 0; i < 5; ++i) {std::cout << arr1[i] << " " << arr2[i] << "\n";}// 数组自动释放return 0;
    }
    
  3. this转换为shared_ptr

    #include <memory>
    #include <iostream>class MyClass : public std::enable_shared_from_this<MyClass> {
    public:std::shared_ptr<MyClass> getShared() {return shared_from_this();}~MyClass() {std::cout << "MyClass destroyed\n";}
    };int main() {auto ptr = std::make_shared<MyClass>();auto ptr2 = ptr->getShared();std::cout << "Reference count: " << ptr.use_count()  << "\n";//Reference count: 2return 0;
    }
    

七、 选择智能指针的准则

  1. 使用std::unique_ptr

    • 资源只有一个拥有者
    • 需要高效的资源管理
    • 需要将所有权转移到其他对象
  2. 使用std::shared_ptr

    • 资源有多个拥有者
    • 无法预先确定哪个拥有者最后使用资源
  3. 使用std::weak_ptr

    • 需要观察共享资源但不参与生命周期管理
    • 需要打破std::shared_ptr的循环引用
  4. 避免使用裸指针管理资源,优先使用智能指针。

http://www.xdnf.cn/news/18790.html

相关文章:

  • IO多路转接(select方案)
  • 测试用例如何评审?
  • `mysql_query()` 数据库查询函数
  • 如何监控ElasticSearch的集群状态?
  • THM trypwnme2
  • 【广告系列】流量归因模型
  • LeetCode热题100--102. 二叉树的层序遍历--中等
  • 云计算学习笔记——Linux用户和组的归属权限管理、附加权限、ACL策略管理篇
  • CentOS安装Jenkins全流程指南
  • 【大白话解析】 OpenZeppelin 的 ECDSA 库:以太坊签名验证安全工具箱(附源代码)
  • 零基础也能写博客:cpolar简化Docsify远程发布流程
  • 自学嵌入式第二十七天:Linux系统编程-进程
  • MQTT 协议模型:客户端、 broker 与主题详解(二)
  • Java 学习笔记(基础篇10)
  • Qwen2-Plus与DeepSeek-V3深度测评:从API成本到场景适配的全面解析
  • Coze用户账号设置修改用户头像-后端源码
  • 大模型的多机多卡训练
  • 09-数据存储与服务开发
  • 深度学习分类网络初篇
  • react+taro打包到不同小程序
  • Nginx与Apache:Web服务器性能大比拼
  • Docker:技巧汇总
  • 连锁零售排班难?自动排班系统来解决
  • Swiper属性全解析:快速掌握滑块视图核心配置!(2.3补充细节,详细文档在uniapp官网)
  • 从C语言到数据结构:保姆级顺序表解析
  • 数据库之两段锁协议相关理论及应用
  • 前端开发:详细介绍npm、pnpm和cnpm分别是什么,使用方法以及之间有哪些关系
  • Ansible 任务控制与事实管理指南:从事实收集到任务流程掌控
  • 面向过程与面向对象
  • AP服务发现中两条重启检测路径