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

相关推荐
..过云雨4 分钟前
13.【Linux系统编程】从ELF格式深入理解动静态库
linux·c语言·c++·后端
长沙红胖子Qt10 分钟前
QGIS开发笔记(五):qgis加载标记点功能,基础标记数量与性能对比测试
c++
百***355118 分钟前
Tomcat10下载安装教程
java
qq_4017004120 分钟前
QT的5种标准对话框
开发语言·qt
一心只读圣贤猪42 分钟前
Canal ES Adapter pkVal 为 null 问题解决方案
java·后端
Bear on Toilet1 小时前
C++_Bug:现代写法拷贝构造中 swap 写法之小坑
数据结构·c++·bug
大头an1 小时前
深入理解Spring核心原理:Bean作用域、生命周期与自动配置完全指南
java·后端
智者知已应修善业1 小时前
【给定英文字符串统计最多小写最前输出】2023-2-27
c语言·开发语言·c++·经验分享·笔记·算法
我的golang之路果然有问题1 小时前
mac配置 unity+vscode的坑
开发语言·笔记·vscode·macos·unity·游戏引擎
铅笔小新z2 小时前
【C++】从理论到实践:类和对象完全指南(上)
开发语言·c++