【智能指针】

C++ 智能指针是 C++11 标准引入的自动内存管理工具,用于解决手动管理动态内存时的内存泄漏、野指针、重复释放等问题。其核心原理是利用 RAII(资源获取即初始化) 机制,将动态内存的生命周期绑定到智能指针的生命周期,当智能指针析构时自动释放所管理的内存

1.uinque_ptr

cpp 复制代码
#include <memory>
#include <iostream>
using namespace std;

/*
特性:独占式所有权,同一时间只能有一个unique_ptr指向某块内存,禁止拷贝(但支持移动)。
适用场景:管理独占资源(如单个对象、数组),避免资源被共享。
*/

class uinque_ptr
{
private:
    /* data */
public:
    uinque_ptr(/* args */)
    {
        cout << "uinque_ptr constructed\n";
    }
    ~uinque_ptr()
    {
        cout << "uinque_ptr destructed\n";
    }
    void display()
    {
        cout << "Inside display function of uinque_ptr\n";
    }
};

int main()
{
    // 创建一个 unique_ptr,管理一个 uinque_ptr 对象
    std::unique_ptr<uinque_ptr> ptr1(new uinque_ptr());

    // 使用 unique_ptr 访问 uinque_ptr 的成员函数
    ptr1->display();

    // 错误:不能复制 unique_ptr 
    // std::unique_ptr<uinque_ptr> ptr2 = ptr1;  ERROR

    // 转移所有权到另一个 unique_ptr
    std::unique_ptr<uinque_ptr> ptr2 = std::move(ptr1);

    if (!ptr1)
    {
        std::cout << "ptr1 is now null after move.\n";
    }

    // 使用新的 unique_ptr 访问 uinque_ptr 的成员函数
    ptr2->display();

    // 当 ptr2 超出作用域时,uinque_ptr 对象会被自动销毁
    return 0;
}

核心原理:

  1. 核心原理
    独占所有权:通过禁用拷贝构造和拷贝赋值,仅允许移动语义,确保同一时间只有一个unique_ptr指向某块内存。
    RAII 绑定生命周期:unique_ptr对象存储一个指向动态内存的裸指针(T*)和一个删除器(Deleter,默认是std::default_delete)。当unique_ptr析构时,自动调用删除器释放所管理的内存。
cpp 复制代码
template <typename T, typename Deleter = std::default_delete<T>>
class unique_ptr {
public:
    // 禁用拷贝构造
    unique_ptr(const unique_ptr&) = delete;
    // 禁用拷贝赋值
    unique_ptr& operator=(const unique_ptr&) = delete;

    // 允许移动构造
    unique_ptr(unique_ptr&& other) noexcept {
        ptr_ = other.ptr_;
        other.ptr_ = nullptr; // 转移所有权后,原指针置空
    }

    // 允许移动赋值
    unique_ptr& operator=(unique_ptr&& other) noexcept {
        if (this != &other) {
            deleter_(ptr_); // 释放当前资源
            ptr_ = other.ptr_;
            other.ptr_ = nullptr;
        }
        return *this;
    }

    // 析构时释放资源
    ~unique_ptr() {
        deleter_(ptr_);
    }

private:
    T* ptr_;
    Deleter deleter_;
};

2.shared_ptr

cpp 复制代码
#include <memory>
#include <iostream>
using namespace std;

/*

特性:共享式所有权,多个shared_ptr可指向同一内存,通过引用计数管理生命周期:
当新的shared_ptr指向对象时,引用计数 + 1;
当shared_ptr析构或重置时,引用计数 - 1;
引用计数为 0 时,自动释放内存。
适用场景:需要共享资源的场景(如多个对象共享同一数据)。

注意:
优先使用std::make_shared创建(更高效,避免内存泄漏);
避免循环引用(否则引用计数无法归零,导致内存泄漏,需配合std::weak_ptr解决)。
*/

class shared_Ptr
{
private:
    /* data */
public:

    shared_Ptr(/* args */)
    {
        cout << "shared_ptr constructed\n";
    }
    ~shared_Ptr()
    {
        cout << "shared_ptr destructed\n";
    }
    void display()
    {
        cout << "Inside display function of shared_ptr\n";
    }
};

int main()
{
    // 创建一个 shared_ptr,管理一个 shared_ptr 对象
    std::shared_ptr<shared_Ptr> ptr1 = std::make_shared<shared_Ptr>();
    // 使用 shared_ptr 访问 shared_ptr 的成员函数
    ptr1->display();
    cout<<"Ref count:"<<ptr1.use_count()<<endl;
    {
        // 复制 shared_ptr,增加引用计数
        std::shared_ptr<shared_Ptr> ptr2 = ptr1;
        cout<<"Ref count after copy:"<<ptr1.use_count()<<endl;
        // 使用新的 shared_ptr 访问 shared_ptr 的成员函数
        ptr2->display();
    }
    cout<<"Ref count after ptr2 goes out of scope:"<<ptr1.use_count()<<endl;
    // 当最后一个 shared_ptr 超出作用域时,shared_Ptr 对象会被自动销毁
    ptr1.reset();
    cout<<"Ref count after reset ptr1:"<<ptr1.use_count()<<endl;




    return 0;
}

1. 核心原理

共享所有权:

通过引用计数(Reference Count) 跟踪指向同一资源的shared_ptr数量,当最后一个shared_ptr析构时,引用计数归零,释放资源。

双指针结构:

shared_ptr内部包含两个指针:

1.资源指针:指向实际管理的动态对象(T*);

2.控制块指针:指向一个独立的控制块(Control Block),控制块存储:

①引用计数(use_count):当前指向资源的shared_ptr数量;

②弱引用计数(weak_count):指向资源的weak_ptr数量;

③删除器(Deleter)和分配器(Allocator);

④资源的析构逻辑。

2. 底层实现关键

控制块的创建:

当通过std::make_shared()创建shared_ptr时,控制块与资源内存一次性分配(更高效);

当通过new T构造shared_ptr时,控制块单独分配。

c 复制代码
template <typename T>
class shared_ptr {
public:
    // 拷贝构造:引用计数+1
    shared_ptr(const shared_ptr& other) {
        if (other.control_block_) {
            control_block_ = other.control_block_;
            control_block_->use_count++; // 共享控制块,引用计数+1
        }
    }

    // 析构:引用计数-1,归零则释放资源
    ~shared_ptr() {
        if (control_block_) {
            control_block_->use_count--;
            if (control_block_->use_count == 0) {
                delete control_block_->ptr; // 释放资源
                if (control_block_->weak_count == 0) {
                    delete control_block_; // 弱引用也归零,释放控制块
                }
            }
        }
    }

private:
    T* ptr_;
    ControlBlock* control_block_; // 指向控制块
};

// 控制块结构(简化版)
struct ControlBlock {
    T* ptr;
    size_t use_count;    // shared_ptr的引用计数
    size_t weak_count;   // weak_ptr的引用计数
    Deleter deleter;
};

3.weak_ptr

cpp 复制代码
#include <memory>
#include <iostream>
using namespace std;

/*
特性:弱引用,不拥有资源所有权,仅观察shared_ptr管理的资源,不影响引用计数。
适用场景:解决shared_ptr的循环引用问题(如双向链表节点、父子对象引用)。
*/

struct Node {
    std::weak_ptr<Node> next; // 弱引用,避免循环引用
    ~Node() { std::cout << "Node destructed\n"; }
};

int main() 
{
    std::shared_ptr<Node> node1 = std::make_shared<Node>();
    cout<<"Ref count of node1 1: "<<node1.use_count()<<endl;
    std::shared_ptr<Node> node2 = std::make_shared<Node>();
    cout<<"Ref count of node2 2: "<<node2.use_count()<<endl;

    node1->next = node2; // 弱引用,node2的引用计数不变
    node2->next = node1; // 弱引用,node1的引用计数不变
    cout<<"Ref count of node1 after linking: "<<node1.use_count()<<endl;

    return 0; // 引用计数为0,正常释放内存
}

1. 核心原理

弱引用

:不拥有资源所有权,仅观察shared_ptr管理的资源,不影响引用计数(use_count),但会关联到shared_ptr的控制块(增加weak_count)。

安全访问

:需通过lock()方法将weak_ptr转换为shared_ptr才能访问资源,若资源已释放(use_count==0),lock()返回空shared_ptr,避免悬空指针。

2. 底层实现关键

关联控制块:weak_ptr存储指向shared_ptr控制块的指针,仅跟踪weak_count:

c 复制代码
template <typename T>
class weak_ptr {
public:
    // 从shared_ptr构造:弱引用计数+1
    weak_ptr(const shared_ptr<T>& other) {
        control_block_ = other.control_block_;
        if (control_block_) {
            control_block_->weak_count++;
        }
    }

    // 析构:弱引用计数-1,归零则释放控制块
    ~weak_ptr() {
        if (control_block_) {
            control_block_->weak_count--;
            if (control_block_->use_count == 0 && control_block_->weak_count == 0) {
                delete control_block_; // 资源和弱引用都归零,释放控制块
            }
        }
    }

    // 转换为shared_ptr(安全访问)
    shared_ptr<T> lock() const {
        if (control_block_ && control_block_->use_count > 0) {
            return shared_ptr<T>(control_block_); // 引用计数+1
        }
        return shared_ptr<T>(); // 资源已释放,返回空
    }

private:
    ControlBlock* control_block_;
};

总结:

四、总结

智能指针 所有权 引用计数 拷贝性 典型场景

unique_ptr 独占 | 无 | 不可拷贝 | 单个对象 / 数组的独占管理

shared_ptr 共享 | 有 | 可拷贝 | 资源共享场景

weak_ptr 弱引用(无)| 无 | 可拷贝 | 解决循环引用

相关推荐
稚辉君.MCA_P8_Java1 小时前
Gemini永久会员 go数组中最大异或值
数据结构·后端·算法·golang·哈希算法
蜗牛攻城狮1 小时前
JavaScript 尾递归(Tail Recursion)详解
开发语言·javascript·ecmascript
Dxy12393102161 小时前
Python的PIL对象crop函数详解
开发语言·python
水饺编程1 小时前
第3章,[标签 Win32] :WM_CREATE 消息的产生
c语言·c++·windows·visual studio
Michelle80231 小时前
机器学习实战操作手册
人工智能·算法·机器学习
坐吃山猪1 小时前
Electron04-系统通知小闹钟
开发语言·javascript·ecmascript
翔云 OCR API1 小时前
护照NFC识读鉴伪接口集成-让身份核验更加智能与高效
开发语言·人工智能·python·计算机视觉·ocr
HaiLang_IT1 小时前
【目标检测】基于卷积神经网络的轨道部件(扣件、轨枕、钢轨)缺陷检测算法研究
算法·目标检测·cnn
草莓熊Lotso1 小时前
《算法闯关指南:优选算法--前缀和》--31.连续数组,32.矩阵区域和
c++·线性代数·算法·矩阵