C++怎么使用智能指针shared_ptr

来源:站长素材作者:小宵头衔:网络博主
导读:本期聚焦于小伙伴创作的《C++怎么使用智能指针shared_ptr》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++怎么使用智能指针shared_ptr》有用,将其分享出去将是对创作者最好的鼓励。

在C++的早期开发中,开发者需要手动调用new和delete管理动态分配的内存,这种方式容易因为忘记释放内存或者重复释放导致内存泄漏、程序崩溃等问题。现代C++引入了智能指针机制,其中shared_ptr是最常用的一种,它通过引用计数自动管理动态对象的生命周期,大幅降低了手动内存管理的出错概率。

shared_ptr的基本使用

创建shared_ptr对象

shared_ptr位于<memory>头文件中,创建shared_ptr最安全的方式是使用make_shared函数,它会一次性分配对象内存和引用计数控制块的内存,效率比先new再构造shared_ptr更高。

#include <memory>
#include <iostream>

int main() {
    // 使用make_shared创建shared_ptr,指向一个int对象,值为10
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
    // 输出指向的值和引用计数
    std::cout << "ptr1指向的值: " << *ptr1 << std::endl;
    std::cout << "当前引用计数: " << ptr1.use_count() << std::endl;
    return 0;
}

shared_ptr的拷贝与赋值

当shared_ptr被拷贝或者赋值时,引用计数会自动增加,当shared_ptr对象销毁时,引用计数会自动减少,当引用计数降为0时,会自动释放所指向的对象内存。

#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
    std::cout << "初始引用计数: " << ptr1.use_count() << std::endl; // 输出1
    
    // 拷贝ptr1,引用计数加1
    std::shared_ptr<int> ptr2 = ptr1;
    std::cout << "拷贝后引用计数: " << ptr1.use_count() << std::endl; // 输出2
    
    // ptr2离开作用域会自动销毁,引用计数减1
    {
        std::shared_ptr<int> ptr3 = ptr1;
        std::cout << "新增拷贝后引用计数: " << ptr1.use_count() << std::endl; // 输出3
    }
    // ptr3销毁后,引用计数回到2
    std::cout << "ptr3销毁后引用计数: " << ptr1.use_count() << std::endl; // 输出2
    
    // 赋值为nullptr,引用计数减1
    ptr2 = nullptr;
    std::cout << "ptr2置空后引用计数: " << ptr1.use_count() << std::endl; // 输出1
    return 0;
}

shared_ptr的底层原理

引用计数控制块

shared_ptr的底层实现依赖一个引用计数控制块,控制块中存储了指向对象的指针、引用计数、弱引用计数等信息。每个被shared_ptr管理的对象都对应一个唯一的控制块,所有指向该对象的shared_ptr都共享这个控制块。

控制块的内存分配时机有两种:使用make_shared时,控制块和对象内存一次性分配;使用new构造shared_ptr时,会单独分配控制块内存。这也是推荐优先使用make_shared的原因之一,能减少内存分配次数。

引用计数的更新逻辑

  • 当新的shared_ptr拷贝已存在的shared_ptr时,引用计数加1
  • 当shared_ptr被销毁(比如离开作用域、被赋值为其他值)时,引用计数减1
  • 当引用计数减为0时,shared_ptr会自动调用删除器释放所指向的对象内存,同时如果弱引用计数也为0,会释放控制块本身的内存

简单的模拟实现

为了更直观理解shared_ptr的原理,我们可以实现一个简化版的MySharedPtr,核心逻辑就是维护引用计数,在构造、拷贝、析构时更新引用计数。

#include <iostream>

// 简化版shared_ptr实现
template <typename T>
class MySharedPtr {
private:
    T* ptr;               // 指向管理的对象
    int* ref_count;       // 指向引用计数,所有拷贝共享同一个计数

public:
    // 构造函数,接收原始指针
    explicit MySharedPtr(T* p = nullptr) : ptr(p), ref_count(nullptr) {
        if (ptr != nullptr) {
            ref_count = new int(1); // 初始引用计数为1
        }
    }

    // 拷贝构造函数
    MySharedPtr(const MySharedPtr<T>& other) : ptr(other.ptr), ref_count(other.ref_count) {
        if (ref_count != nullptr) {
            (*ref_count)++; // 引用计数加1
        }
    }

    // 析构函数
    ~MySharedPtr() {
        if (ref_count != nullptr) {
            (*ref_count)--; // 引用计数减1
            if (*ref_count == 0) {
                // 引用计数为0,释放对象和控制块
                delete ptr;
                delete ref_count;
                ptr = nullptr;
                ref_count = nullptr;
            }
        }
    }

    // 重载->运算符,方便访问对象成员
    T* operator->() const {
        return ptr;
    }

    // 重载*运算符,方便解引用
    T& operator*() const {
        return *ptr;
    }

    // 获取当前引用计数
    int use_count() const {
        return ref_count == nullptr ? 0 : *ref_count;
    }
};

// 测试代码
int main() {
    MySharedPtr<int> myPtr(new int(30));
    std::cout << "初始引用计数: " << myPtr.use_count() << std::endl; // 输出1
    
    MySharedPtr<int> myPtr2 = myPtr;
    std::cout << "拷贝后引用计数: " << myPtr.use_count() << std::endl; // 输出2
    
    std::cout << "指向的值: " << *myPtr << std::endl; // 输出30
    return 0;
}

shared_ptr的使用注意事项

避免循环引用

如果两个对象互相持有对方的shared_ptr,会形成循环引用,导致引用计数永远无法降为0,从而产生内存泄漏。这种情况需要使用weak_ptr来打破循环。

#include <memory>
#include <iostream>

class B; // 向前声明

class A {
public:
    std::shared_ptr<B> b_ptr;
    ~A() {
        std::cout << "A对象销毁" << std::endl;
    }
};

class B {
public:
    std::shared_ptr<A> a_ptr; // 这里如果换成std::weak_ptr<A>就能打破循环
    ~B() {
        std::cout << "B对象销毁" << std::endl;
    }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->b_ptr = b;
    b->a_ptr = a; // 循环引用,离开作用域后A和B都不会被销毁
    std::cout << "a引用计数: " << a.use_count() << std::endl; // 输出2
    std::cout << "b引用计数: " << b.use_count() << std::endl; // 输出2
    return 0;
}

不要将原始指针和多个shared_ptr混用

同一个原始指针不要用来构造多个独立的shared_ptr,否则会导致每个shared_ptr都有自己独立的引用计数,最终会重复释放同一块内存,引发程序崩溃。

#include <memory>

int main() {
    int* raw_ptr = new int(40);
    // 错误用法:两个shared_ptr独立管理同一个原始指针
    std::shared_ptr<int> ptr1(raw_ptr);
    std::shared_ptr<int> ptr2(raw_ptr); // 会导致重复释放,程序崩溃
    return 0;
}

优先使用make_shared

如前文所述,make_shared能一次性分配对象和控制块的内存,减少内存分配次数,同时能避免在构造shared_ptr之前发生异常导致的内存泄漏,是创建shared_ptr的首选方式。

shared_ptr作为现代C++内存管理的重要工具,合理运用能大幅减少内存相关的bug,开发者需要理解其原理和注意事项,才能在实际开发中正确发挥它的作用。

shared_ptr智能指针现代内存管理引用计数修改时间:2026-06-22 15:13:10

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。