智能指针
编程 C++ 13

C/C++语言因为能够使用指针更方便的操作内存空间,因此速度相较于其它语言更快。但是也带来了该语言的致命缺陷——内存泄露。智能指针是C++11中提出的概念,采用了RALL(Resource Acquisition Is Initialization,资源分配即初始化)的设计思想,主要是为了解决C++代码中的内存泄露问题。目前使用的智能指针有三种,分别是unique_ptr、shared_ptr和weak_ptr。使用智能指针之前,需要添加头文#include <memory>

一、raw_ptr

原始指针,也称为裸指针,语法如下:

Obj* obj = new Obj();
obj->doSomething();
delete obj;
obj = nullptr;

在使用new创建一个指针对象后,当不再使用该对象之后,需要及时delete。否则该块堆内存被一直占用,导致资源浪费,甚至造车给程序崩溃。

智能指针采用RALL设计思想,使用模板,对裸指针进行了封装,时智能指针在离开自己的作用域范围时能够自己调用析构函数,释放裸指针。

二、unique_ptr

  • 独占所有权:unique_ptr称为独占智能指针,顾名思义,一旦该指针被初始化指向某对象,该对象就不允许再被其它指针获取。智能通过移动语义来转移对象的所有权。

  • 特点:与裸指针在性能上差不多,离开作用域时自动调用 delete 或自定义的删除器。

为了实现智能指针,需要禁用拷贝构造函数和拷贝赋值函数。实现如下:

#include <iostream>

template <typename T>
class my_unique_ptr
{
private:
    T* raw_ptr;
public:
    explicit my_unique_ptr(T* ptr = nullptr) : raw_ptr(ptr) {}
    ~my_unique_ptr() {
        delete raw_ptr;
    }

    // 禁止拷贝
    my_unique_ptr(const my_unique_ptr&) = delete;
    my_unique_ptr& operator=(const my_unique_ptr&) = delete;

    // 移动语义
    my_unique_ptr(my_unique_ptr&& other) : raw_ptr(other.raw_ptr) {
        other.raw_ptr = nullptr;
    }
    my_unique_ptr& operator=(my_unique_ptr&& other) {
        if (this != &other) {
            delete raw_ptr;
            raw_ptr = other.raw_ptr;
            other.raw_ptr = nullptr;
        }
        return *this; // 添加返回语句
    }

    // 解引用操作
    T& operator*() const { return *raw_ptr; }
    T* operator->() const { return raw_ptr; }

    // get
    T* get() const {
        return raw_ptr;
    }

    // release but do not release memory
    T* release() {
        T* temp = raw_ptr;
        raw_ptr = nullptr;
        return temp;
    }

    // reset
    void reset(T* ptr = nullptr) {
        delete raw_ptr;
        raw_ptr = ptr; // 修正参数名称
    }
};

三、shared_ptr

  • 共享所有权:通过引用计数跟踪有多少个 shared_ptr 指向同一对象,当计数归零时自动释放内存。

  • 用途:需要多个指针共享同一资源的场景。

  • 特点

    • 支持拷贝和移动语义。

    • 线程安全(引用计数操作是原子的,但对象访问需额外同步)。

    • 循环引用会导致内存泄漏(需配合 std::weak_ptr 解决)。

什么是循环引用?在使用shared_ptr时,两个类A和B,A引用B,B同时引用A,导致引用计数不能置零,造成内存泄露。使用weak_ptr可以避免循环引用。

shared_ptr的核心是共享所有权,通过引用计数(Reference Counting)跟踪有多少个shared_ptr指向了同一个对象。当引用计数归零时,自动释放内存。

实现shared_ptr的关键有两个部分:

  1. 控制块(Control Block)

  • 存储引用计数,强引用,shared_ptr使用

  • 存储弱引用,weak_ptr使用

  • 存储自定义删除器

  • 存储原始指针,指向被管理的对象

  1. 引用计数

  • 每次shared_ptr拷贝构造或赋值时,强引用计数加一

  • 每次shared_ptr析构时,强引用计数减一,归零时,调用删除器释放内存。

#include <iostream>
#include <atomic>

template<typename T>
class SharedPtr; // 前向声明

template<typename T>
class WeakPtr;   // 前向声明

// 控制块类
template<typename T>
class ControlBlock {
public:
    T* object;
    std::atomic<int> strong_count;  // 使用原子类型
    std::atomic<int> weak_count;    // 使用原子类型
    
    explicit ControlBlock(T* obj) 
        : object(obj), strong_count(1), weak_count(0) {}
    
    ~ControlBlock() {
        delete object;
    }
};

template<typename T>
class SharedPtr {
private:
    T* ptr;
    ControlBlock<T>* control;
    
    // 自定义的删除器
    void cleanup() {
        if (!control) return;
        
        if (--control->strong_count == 0) {
            // 对象销毁,但保留控制块
            control->object = nullptr;
            
            if (control->weak_count == 0) {
                delete control;
            }
        }
    }
    
public:
    explicit SharedPtr(T* p = nullptr) 
        : ptr(p), control(p ? new ControlBlock<T>(p) : nullptr) {}
    
    // 拷贝构造
    SharedPtr(const SharedPtr& other) 
        : ptr(other.ptr), control(other.control) {
        if (control) control->strong_count++;
    }
    
    // 从WeakPtr构造
    explicit SharedPtr(const WeakPtr<T>& weak) 
        : ptr(weak.ptr), control(weak.control) {
        if (control) control->strong_count++;
    }
    
    // 析构
    ~SharedPtr() { cleanup(); }
    
    // 其他成员函数...
};

四、weak_ptr

weak_ptr 不增加强引用计数,仅增加弱引用计数。它用于观察 shared_ptr 管理的资源而不阻止其释放。

关键点

  • 不增加强引用计数weak_ptr 的拷贝/赋值不会影响对象的生命周期。

  • 通过 lock() 获取 shared_ptr

    • lock() 会检查强引用计数是否 >0,如果是则返回 shared_ptr(并增加强引用计数)。

    • 如果强引用计数 =0(对象已释放),返回 nullptr

template<typename T>
class WeakPtr {
private:
    T* ptr;
    ControlBlock<T>* control;
    
    // 自定义的删除器
    void cleanup() {
        if (!control) return;
        
        if (--control->weak_count == 0 && control->strong_count == 0) {
            delete control;
        }
    }
    
public:
    // 默认构造
    WeakPtr() : ptr(nullptr), control(nullptr) {}
    
    // 从SharedPtr构造
    WeakPtr(const SharedPtr<T>& shared) 
        : ptr(shared.get()), control(shared.control) {
        if (control) control->weak_count++;
    }
    
    // 拷贝构造
    WeakPtr(const WeakPtr& other) 
        : ptr(other.ptr), control(other.control) {
        if (control) control->weak_count++;
    }
    
    // 析构
    ~WeakPtr() { cleanup(); }
    
    // 转换为SharedPtr
    SharedPtr<T> lock() const {
        return control && control->strong_count > 0 
            ? SharedPtr<T>(*this) 
            : SharedPtr<T>();
    }
    
    // 其他成员函数...
};

智能指针
http://47.92.222.121:8090/archives/tZxeYi9F
作者
禧语许
发布于
更新于
许可