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

相关推荐
崇山峻岭之间15 分钟前
C++ Prime Plus 学习笔记027
c++·笔记·学习
小石头 1008619 分钟前
【JavaEE】进程和线程的区别
java·java-ee
赖small强25 分钟前
【Linux C/C++开发】Linux C/C++ 堆栈溢出:原理、利用与防护深度指南
linux·c语言·c++·stack·堆栈溢出
爱学习的梵高先生27 分钟前
C++:基础知识
开发语言·c++·算法
oioihoii31 分钟前
C++对象生命周期与析构顺序深度解析
java·开发语言·c++
IMPYLH34 分钟前
Lua 的 tonumber 函数
开发语言·笔记·后端·junit·游戏引擎·lua
xlq2232234 分钟前
24.map set(下)
数据结构·c++·算法
BBB努力学习程序设计42 分钟前
Java枚举(Enum):定义固定值的"类型安全"利器
java
晚风吹长发1 小时前
初步了解Linux中文件描述符-fd
linux·运维·服务器·c++·开发·文件
It's now1 小时前
BeanRegistrar 的企业级应用场景及最佳实践
java·开发语言·spring