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++ 内存管理的核心工具,正确使用可以显著提高代码的安全性和可维护性。

相关推荐
Anastasiozzzz2 分钟前
Java Lambda 揭秘:从匿名内部类到底层原理的深度解析
java·开发语言
骇客野人4 分钟前
通过脚本推送Docker镜像
java·docker·容器
刘琦沛在进步6 分钟前
【C / C++】引用和函数重载的介绍
c语言·开发语言·c++
机器视觉的发动机18 分钟前
AI算力中心的能耗挑战与未来破局之路
开发语言·人工智能·自动化·视觉检测·机器视觉
铁蛋AI编程实战21 分钟前
通义千问 3.5 Turbo GGUF 量化版本地部署教程:4G 显存即可运行,数据永不泄露
java·人工智能·python
HyperAI超神经26 分钟前
在线教程|DeepSeek-OCR 2公式/表格解析同步改善,以低视觉token成本实现近4%的性能跃迁
开发语言·人工智能·深度学习·神经网络·机器学习·ocr·创业创新
晚霞的不甘32 分钟前
CANN 编译器深度解析:UB、L1 与 Global Memory 的协同调度机制
java·后端·spring·架构·音视频
SunnyDays101134 分钟前
使用 Java 冻结 Excel 行和列:完整指南
java·冻结excel行和列
R_.L36 分钟前
【QT】常用控件(按钮类控件、显示类控件、输入类控件、多元素控件、容器类控件、布局管理器)
开发语言·qt
Zach_yuan1 小时前
自定义协议:实现网络计算器
linux·服务器·开发语言·网络