跳转至

8 Smart Pointers

文本统计:约 469 个字 • 174 行代码

普通指针管理内存容易造成内存泄漏(比如用完忘记释放),C++提供了智能指针用于内存的管理。

智能指针使用了一种RAII(资源获取即初始化)技术对普通的指针进行了封装,使得智能指针实质上是一个对象,但是行为表现得像一个指针

下面是一个 unique_ptr 类似的实现,u_ptr

template<typename T>
class u_ptr{
public:
    explicit u_ptr(T *p = nullptr) : ptr(p) {}  //构造函数显示调用

    u_ptr(const u_ptr&) = delete; // 禁止拷贝构造函数
    u_ptr& operator=(const u_ptr&) = delete; // 禁止拷贝赋值

    u_ptr(u_ptr&& other) noexcept: ptr(other.release()) {} // 移动构造函数
    u_ptr& operator=(u_ptr&& other) noexcept { // 移动赋值运算符
        if (this != &other) {
            reset(other.release()); // 释放当前资源
        }
        return *this;
    }

    ~u_ptr() {
        delete ptr;
    }
    T* operator->() const{
        return ptr;
    }
    T& operator*() const{
        return *ptr;
    }
    T* get() const{
        return ptr;
    }

    T* release() {
        T *temp = ptr;
        ptr = nullptr; // 释放所有权
        return temp;
    }

    void reset(T *p = nullptr) {
        delete ptr; // 释放原有资源
        ptr = p; // 接管新资源
    }
private:
    T *ptr;
};

std::unique_ptr

  • 独占所有权unique_ptr 表示对其所指向的对象具有唯一的所有权,这意味着在任何给定的时间点只能有一个 unique_ptr 指向某个对象。
  • 禁止复制:由于其独占所有权的特性,unique_ptr 不能被复制构造或赋值操作(即你不能简单地将一个 unique_ptr 赋值给另一个 unique_ptr)。
  • 支持移动语义:可以通过移动构造函数和移动赋值操作来转移所有权。
unique_ptr<Resource> p1 (new Resource);
unique_ptr<Resource> p2(new Resource(7));

//p1 = p2; 这样多个智能指针管理同一个资源会导致错误,因为是 unique_ptr
p1 = move(p2); // 这里是将 p2 的所有权转移给 p1,p1的资源也释放掉了

关于 std::move 与 右值引用

一文读懂C++右值引用和std::move - 知乎

下面是一个 shared_ptr 类似的 s_ptr 的实现

template<typename T>
class s_ptr{
private:
    struct ControlBlock{
        int refCount;
        T *ptr;
        ControlBlock(T *p) : refCount(1), ptr(p) {}
    };

    ControlBlock *controlBlock;
    void add_shared(){
        if (controlBlock){
            ++controlBlock->refCount;
        }
    }

    void release_shared(){
        if (controlBlock){
            --controlBlock->refCount;
            if (controlBlock->refCount == 0){
                delete controlBlock->ptr; // 释放资源
                delete controlBlock; // 释放控制块
            }
        }
    }

public:
    explicit s_ptr(T *p = nullptr) {
        if (p)
            controlBlock = new ControlBlock(p);
        else
            controlBlock = nullptr;
    }

    void reset(T *p = nullptr) {
        s_ptr(p).swap(*this); // 交换两个智能指针
    }

    s_ptr(const s_ptr& other) : controlBlock(other.controlBlock) {
        add_shared();
    }

    s_ptr& operator=(const s_ptr& other) {
        s_ptr(other).swap(*this); // 交换两个智能指针
        return *this;

        // s_ptr(other)这是一个临时对象,调用了拷贝构造函数
        // 交换后,临时对象会被销毁,减少引用计数

        /*
        if (this != &other) {
            release();
            controlBlock = other.controlBlock;
            add_shared();
        }
        return *this;*/
    }

    s_ptr(s_ptr&& other) noexcept : controlBlock(other.controlBlock) {
        other.controlBlock = nullptr;
    }

    s_ptr& operator=(s_ptr&& other) noexcept { 
        if (this != &other) {
            release_shared();
            controlBlock = other.controlBlock;
            other.controlBlock = nullptr;
        }
    }

    void swap(s_ptr& other) noexcept{
        std::swap(controlBlock, other.controlBlock);
    }

    ~s_ptr() {
        release_shared();
    }

    T* operator->() const {
        return controlBlock->ptr;
    }
    T& operator*() const {
        return *(controlBlock->ptr);
    }

    T* get() const {
        return controlBlock ? controlBlock->ptr : nullptr;
    }

    int use_count() const {
        return controlBlock ? controlBlock->refCount : 0;
    }
};

std::shared_ptr非独占的指针,可以多个指针管理一个对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全

  • 可以用use_count函数来查看某个对象拥有的指针的个数
  • 资源对象在所有管理者释放时自动释放资源
shared_ptr<Resource> p2(new Resource(7));

{
    shared_ptr<Resource> p1(new Resource(1));
    cout<< "p1:" << p1.get() << endl;
    cout<< "p1 data: " << p1->data << endl;
    cout<< "p1 use_count: " << p1.use_count() << endl;
    //shared_ptr<Resource> p2 (p1); // 共享所有权
    p2 = move(p1);
    cout<< "p2:" << p2.get() << endl;
    cout<< "p2 data: " << p2->data << endl;
    cout<< "p2 use_count: " << p2.use_count() << endl;
}
//shared_ptr 在最后一个管理者释放时会自动释放资源

这样输出为

Resource acquired: 7
Resource acquired: 1
p1:0xf24680
p1 data: 1
p1 use_count: 1
Resource released: 7
p2:0xf24680
p2 data: 1
p2 use_count: 1
Resource released: 1

注意区分 p2 = p1p2 = move(p1) 的区别,一种是增加管理者,另一种是管理权转移。改为 p2 = p1 输出即为:

Resource acquired: 7
Resource acquired: 1
p1:0x6d4680
p1 data: 1
p1 use_count: 1
Resource released: 7
p2:0x6d4680
p2 data: 1
p2 use_count: 2
Resource released: 1

评论区

对你有帮助的话请给我个赞和 star => GitHub stars
欢迎跟我探讨!!!