C++智能指针使用指南(auto_ptr, unique_ptr, shared_ptr, weak_ptr)

在 C++ 中,智能指针是管理动态分配内存的重要工具,它们可以自动释放内存,避免内存泄漏。以下是四种主要智能指针的详细介绍:

1. auto_ptr (已废弃)

基本特性

  • C++98 引入,C++17 中已移除

  • 具有所有权转移语义

示例代码

cpp

复制代码
#include <memory>
#include <iostream>

void autoPtrExample() {
    std::auto_ptr<int> p1(new int(10));
    std::cout << *p1 << std::endl;  // 输出: 10
    
    std::auto_ptr<int> p2 = p1;  // 所有权转移
    // std::cout << *p1 << std::endl;  // 错误!p1 现在为空
    std::cout << *p2 << std::endl;  // 输出: 10
}

问题

  • 所有权转移不明确,容易导致错误

  • 不支持数组

  • 不能在 STL 容器中使用

2. unique_ptr (C++11)

基本特性

  • 独占所有权,不能拷贝,只能移动

  • 轻量级,无额外开销

  • 支持自定义删除器

  • 支持数组

示例代码

cpp

复制代码
#include <memory>
#include <iostream>

void uniquePtrExample() {
    // 基本用法
    std::unique_ptr<int> p1(new int(20));
    std::cout << *p1 << std::endl;  // 输出: 20
    
    // 移动语义
    std::unique_ptr<int> p2 = std::move(p1);
    std::cout << *p2 << std::endl;  // 输出: 20
    // p1 现在为空
    
    // 使用 make_unique (C++14)
    auto p3 = std::make_unique<int>(30);
    
    // 数组支持
    auto p4 = std::make_unique<int[]>(5);
    p4[0] = 1;
    p4[1] = 2;
    
    // 自定义删除器
    auto deleter = [](int* p) {
        std::cout << "Deleting int: " << *p << std::endl;
        delete p;
    };
    std::unique_ptr<int, decltype(deleter)> p5(new int(50), deleter);
}

3. shared_ptr (C++11)

基本特性

  • 共享所有权,使用引用计数

  • 支持拷贝和赋值

  • 线程安全(引用计数操作)

  • 支持自定义删除器

示例代码

cpp

复制代码
#include <memory>
#include <iostream>

class MyClass {
public:
    MyClass(int val) : value(val) {
        std::cout << "MyClass constructed: " << value << std::endl;
    }
    
    ~MyClass() {
        std::cout << "MyClass destroyed: " << value << std::endl;
    }

    
    void print() {
        std::cout << "Value: " << value << std::endl;
    }
    
private:
    int value;
};

void sharedPtrExample() {
    // 创建 shared_ptr
    std::shared_ptr<MyClass> p1 = std::make_shared<MyClass>(100);
rd.xjyl.gov.cn/upload/1982074929729208321.html
rd.xjyl.gov.cn/upload/1982074929775345664.html
rd.xjyl.gov.cn/upload/1982074929792122880.html
rd.xjyl.gov.cn/upload/1982074929871814656.html
rd.xjyl.gov.cn/upload/1982074929980866560.html
rd.xjyl.gov.cn/upload/1982074930068946944.html
rd.xjyl.gov.cn/upload/1982074930337382400.html
rd.xjyl.gov.cn/upload/1982074930408685568.html
rd.xjyl.gov.cn/upload/1982074930429657088.html
rd.xjyl.gov.cn/upload/1982074930551291904.html
rd.xjyl.gov.cn/upload/1982074930614206464.html
rd.xjyl.gov.cn/upload/1982074930635177984.html
rd.xjyl.gov.cn/upload/1982074930706481152.html
rd.xjyl.gov.cn/upload/1982074930719064064.html
rd.xjyl.gov.cn/upload/1982074931075579904.html
rd.xjyl.gov.cn/upload/1982074931146883072.html
rd.xjyl.gov.cn/upload/1982074931247546368.html
rd.xjyl.gov.cn/upload/1982074931247546369.html
rd.xjyl.gov.cn/upload/1982074931373375488.html
rd.xjyl.gov.cn/upload/1982074931369181185.html
rd.xjyl.gov.cn/upload/1982074931369181184.html
rd.xjyl.gov.cn/upload/1982074931570507776.html
rd.xjyl.gov.cn/upload/1982074931604062208.html
rd.xjyl.gov.cn/upload/1982074931725697024.html
    
    {
        std::shared_ptr<MyClass> p2 = p1;  // 引用计数 +1
        p2->print();
        std::cout << "Use count: " << p1.use_count() << std::endl;  // 输出: 2
    }  // p2 析构,引用计数 -1
    
    std::cout << "Use count: " << p1.use_count() << std::endl;  // 输出: 1
    p1->print();
    
    // 自定义删除器
    std::shared_ptr<MyClass> p3(new MyClass(200), 
        [](MyClass* p) {
            std::cout << "Custom deleter called" << std::endl;
            delete p;
        });
}

4. weak_ptr (C++11)

基本特性

  • 不拥有对象所有权

  • 解决 shared_ptr 循环引用问题

  • 必须从 shared_ptr 创建

循环引用问题示例

cpp

复制代码
#include <memory>
#include <iostream>

class Node {
public:
    std::shared_ptr<Node> next;
    std::shared_ptr<Node> prev;
    
    ~Node() {
        std::cout << "Node destroyed" << std::endl;
    }
};

void circularReferenceProblem() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();
    
    node1->next = node2;  // 循环引用
    node2->prev = node1;  // 内存泄漏!
    
    // node1 和 node2 的引用计数永远不会为 0
}

使用 weak_ptr 解决循环引用

cpp

复制代码
class NodeSafe {
public:
    std::shared_ptr<NodeSafe> next;
    std::weak_ptr<NodeSafe> prev;  // 使用 weak_ptr
    
    ~NodeSafe() {
        std::cout << "NodeSafe destroyed" << std::endl;
rd.xjyl.gov.cn/upload/1982074931901857792.html
rd.xjyl.gov.cn/upload/1982074931914440704.html
rd.xjyl.gov.cn/upload/1982074932023492608.html
rd.xjyl.gov.cn/upload/1982074932027686912.html
rd.xjyl.gov.cn/upload/1982074932086407168.html
rd.xjyl.gov.cn/upload/1982074932115767296.html
rd.xjyl.gov.cn/upload/1982074932203847680.html
rd.xjyl.gov.cn/upload/1982074932262567936.html
rd.xjyl.gov.cn/upload/1982074932291928064.html
rd.xjyl.gov.cn/upload/1982074932317093888.html
rd.xjyl.gov.cn/upload/1982074932392591360.html
rd.xjyl.gov.cn/upload/1982074932367425536.html
rd.xjyl.gov.cn/upload/1982074932392591361.html
rd.xjyl.gov.cn/upload/1982074932640055296.html
rd.xjyl.gov.cn/upload/1982074932661026816.html
rd.xjyl.gov.cn/upload/1982074932728135680.html
rd.xjyl.gov.cn/upload/1982074932954628096.html
rd.xjyl.gov.cn/upload/1982074932958822400.html
rd.xjyl.gov.cn/upload/1982074933034319872.html
rd.xjyl.gov.cn/upload/1982074933306949632.html
rd.xjyl.gov.cn/upload/1982074933361475584.html
rd.xjyl.gov.cn/upload/1982074933424390144.html
rd.xjyl.gov.cn/upload/1982074933432778752.html
rd.xjyl.gov.cn/upload/1982074933508276224.html
rd.xjyl.gov.cn/upload/1982074933596356608.html
rd.xjyl.gov.cn/upload/1982074933575385088.html
    }
};

void weakPtrSolution() {
    auto node1 = std::make_shared<NodeSafe>();
    auto node2 = std::make_shared<NodeSafe>();
    
    node1->next = node2;
    node2->prev = node1;  // 不会增加引用计数
    
    // 可以正常析构,无内存泄漏
}

void weakPtrUsage() {
    auto shared = std::make_shared<int>(42);
    std::weak_ptr<int> weak = shared;
    
    // 检查对象是否还存在
    if (auto locked = weak.lock()) {
        std::cout << "Object exists: " << *locked << std::endl;
    } else {
        std::cout << "Object has been destroyed" << std::endl;
    }
    
    shared.reset();  // 释放对象
    
    if (auto locked = weak.lock()) {
        std::cout << "Object exists: " << *locked << std::endl;
    } else {
        std::cout << "Object has been destroyed" << std::endl;
    }
}

最佳实践总结

  1. 优先使用 unique_ptr

    • 默认选择,性能最好

    • 明确表达独占所有权

  2. 需要共享所有权时使用 shared_ptr

    • 多个对象需要访问同一资源

    • 注意避免循环引用

  3. 使用 weak_ptr 打破循环引用

    • 观察者模式

    • 缓存场景

  4. 使用 make_shared 和 make_unique

    • 更安全,避免显式 new

    • 更好的性能(make_shared)

  5. 避免使用 auto_ptr

    • 已被废弃,使用 unique_ptr 替代

完整示例

cpp

复制代码
#include <memory>
#include <iostream>
#include <vector>

int main() {
    // unique_ptr 示例
    std::unique_ptr<int> unique = std::make_unique<int>(10);
    
    // shared_ptr 示例
    std::shared_ptr<int> shared1 = std::make_shared<int>(20);
    std::shared_ptr<int> shared2 = shared1;
    
    // weak_ptr 示例
    std::weak_ptr<int> weak = shared1;
    
    // 在容器中使用智能指针
    std::vector<std::shared_ptr<int>> vec;
    vec.push_back(std::make_shared<int>(1));
    vec.push_back(std::make_shared<int>(2));
    vec.push_back(std::make_shared<int>(3));
    
    for (const auto& ptr : vec) {
        std::cout << *ptr << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

智能指针是现代 C++ 内存管理的核心工具,正确使用可以显著提高代码的安全性和可维护性。

相关推荐
SeaTunnel9 小时前
从 Python Script 地狱到标准化数据集成框架
大数据·开发语言·python·程序员·代码·seatunnel
碎光拾影9 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Marst Code10 小时前
(python)2026Plotly 库评估:交互式可视化到底值不值得引入?
开发语言·python
Miao1213110 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
CRMEB系统商城11 小时前
开源自建还是SaaS订阅?算一笔3年经济账
java·大数据·开发语言·开源
王维同学12 小时前
[自学][Windows C++]RunOnceEx 注册表分组键的安全遍历
c++·windows·安全·开源
keyipatience12 小时前
线程栈与TLS和线程互斥
java·linux·服务器·开发语言·ubuntu
我的xiaodoujiao12 小时前
快速学习Python基础知识详细图文教程9--函数进阶
开发语言·python·学习·测试工具
weixin_4080996713 小时前
2026 图片去水印 API 接口完全指南:一键去除图片水印(附 Python/Java/PHP/C# 示例)
java·python·php·图片处理·api调用·图片去水印·石榴智能
Scott9999HH13 小时前
告别流量波动玄学!从法拉第电磁定律到底层 C/C++ 流量累积算法,深度解密工业级电磁流量计选型与开发
c语言·c++·算法