跳转到内容

C++ shared ptr 详解

来自代码酷


C++ shared_ptr 是 C++11 标准引入的智能指针之一,用于管理动态分配的内存。它通过引用计数机制实现资源的自动释放,有效避免了内存泄漏问题。本文将从基础概念到高级用法,全面解析 `shared_ptr` 的工作原理和使用方法。

概述[编辑 | 编辑源代码]

`shared_ptr` 是一种共享所有权的智能指针,多个 `shared_ptr` 可以指向同一个对象,并通过内部的引用计数器跟踪对象的引用次数。当最后一个 `shared_ptr` 被销毁时,对象会被自动删除。

核心特性[编辑 | 编辑源代码]

  • 引用计数:记录当前有多少个 `shared_ptr` 共享同一对象
  • 线程安全:引用计数的增减操作是原子性的(但对象访问仍需额外同步)
  • 自定义删除器:支持指定自定义的删除逻辑
  • 弱引用支持:可与 `weak_ptr` 配合使用,解决循环引用问题

基本用法[编辑 | 编辑源代码]

创建 shared_ptr[编辑 | 编辑源代码]

#include <memory>
#include <iostream>

int main() {
    // 方式1:使用make_shared(推荐)
    std::shared_ptr<int> p1 = std::make_shared<int>(42);
    
    // 方式2:直接构造
    std::shared_ptr<int> p2(new int(100));
    
    // 方式3:通过reset
    std::shared_ptr<int> p3;
    p3.reset(new int(200));
    
    std::cout << *p1 << " " << *p2 << " " << *p3 << std::endl;
    return 0;
}

输出

42 100 200

引用计数机制[编辑 | 编辑源代码]

可以通过 `use_count()` 方法查看当前引用计数:

#include <memory>
#include <iostream>

void showCount(std::shared_ptr<int> p) {
    std::cout << "count: " << p.use_count() << std::endl;
}

int main() {
    auto p = std::make_shared<int>(10);
    std::cout << "count after creation: " << p.use_count() << std::endl; // 1
    
    {
        auto p2 = p;
        std::cout << "count after copy: " << p.use_count() << std::endl; // 2
        showCount(p); // 函数内会再创建一个拷贝,count: 3
    }
    
    std::cout << "count after scope: " << p.use_count() << std::endl; // 1
    return 0;
}

输出

count after creation: 1
count after copy: 2
count: 3
count after scope: 1

高级特性[编辑 | 编辑源代码]

自定义删除器[编辑 | 编辑源代码]

`shared_ptr` 允许指定自定义的删除逻辑:

#include <memory>
#include <iostream>

void customDeleter(int* p) {
    std::cout << "Custom deleter called for " << *p << std::endl;
    delete p;
}

int main() {
    std::shared_ptr<int> p(new int(99), customDeleter);
    // 当p离开作用域时,customDeleter会被调用
    return 0;
}

输出

Custom deleter called for 99

数组支持[编辑 | 编辑源代码]

C++17 起支持数组类型的 `shared_ptr`:

// C++17及以上版本
std::shared_ptr<int[]> arr(new int[10]);
arr[0] = 42; // 可以直接使用下标操作

循环引用问题[编辑 | 编辑源代码]

`shared_ptr` 可能导致循环引用,这时需要使用 `weak_ptr` 来打破循环:

graph LR A[shared_ptr A] --> B[Object B] B --> C[shared_ptr C] C --> A

示例解决方案:

#include <memory>
#include <iostream>

struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // 使用weak_ptr打破循环
    
    ~Node() { std::cout << "Node destroyed\n"; }
};

int main() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();
    
    node1->next = node2;
    node2->prev = node1; // 使用weak_ptr而非shared_ptr
    
    // 当离开作用域时,node1和node2都能正确释放
    return 0;
}

性能考虑[编辑 | 编辑源代码]

  • make_shared 的优势:单次内存分配(对象和控制块),比直接 `new` 更高效
  • 原子操作开销:引用计数的修改需要原子操作,有一定性能开销
  • 内存占用:每个 `shared_ptr` 需要存储指向控制块的指针

实际应用案例[编辑 | 编辑源代码]

资源管理[编辑 | 编辑源代码]

管理数据库连接等需要自动释放的资源:

class DatabaseConnection {
public:
    static std::shared_ptr<DatabaseConnection> create() {
        return std::shared_ptr<DatabaseConnection>(new DatabaseConnection(), 
            [](DatabaseConnection* conn) {
                conn->close();
                delete conn;
            });
    }
    
    void query(const std::string& sql) { /* ... */ }
    void close() { /* ... */ }
    
private:
    DatabaseConnection() { /* 建立连接 */ }
};

void processData() {
    auto db = DatabaseConnection::create();
    db->query("SELECT * FROM users");
    // 不需要手动关闭连接,shared_ptr会确保资源释放
}

与裸指针的交互[编辑 | 编辑源代码]

获取原始指针[编辑 | 编辑源代码]

使用 `get()` 方法,但要注意生命周期管理:

void legacyFunction(int* p) { /* ... */ }

int main() {
    auto ptr = std::make_shared<int>(42);
    legacyFunction(ptr.get()); // 安全,只要ptr存在
    
    // 危险示例
    int* dangerous = nullptr;
    {
        auto temp = std::make_shared<int>(100);
        dangerous = temp.get();
    } // temp被销毁
    // dangerous现在悬垂指针!
    return 0;
}

最佳实践[编辑 | 编辑源代码]

1. 优先使用 `make_shared` 而非直接 `new` 2. 避免将 `shared_ptr` 的原始指针暴露给外部 3. 对于可能产生循环引用的场景使用 `weak_ptr` 4. 在多线程环境中,仍需对共享对象本身进行同步保护 5. 不要将 `this` 指针直接转换为 `shared_ptr`(使用 `enable_shared_from_this`)

数学表示[编辑 | 编辑源代码]

引用计数机制可以表示为: RC(o)=i=1n{1当 shared_ptri 指向 o0否则RC(o)=0 时,对象 o 被销毁。

总结[编辑 | 编辑源代码]

`shared_ptr` 是 C++ 中强大的资源管理工具,通过引用计数自动管理对象生命周期。正确使用它可以显著减少内存泄漏问题,但需要注意循环引用和线程安全等潜在问题。结合 `make_shared`、`weak_ptr` 等配套工具,可以构建出既安全又高效的资源管理系统。