当前位置: 首页 > article >正文

C++:智能指针

C++在用引用取缔掉指针的同时,模板的引入带给了指针新的发挥空间
智能指针简单的来说就是带有不同特性和内存管理的指针模板

  • unique_ptr
    1.不能有多个对象指向一块内存
    2.对象释放时内部指针指向地址也随之释放
    3.对象内数据只能通过接口更改绑定
    4.对象只能接收右值或者将亡值
  • shared_ptr
    1.可以有多个指针对象指向一块地址
    2.使用一块堆区空间维护一块地址被指向的次数
    3.当指向一个地址的指针数量变为0时清除这块空间
  • weak_ptr
    和shared_ptr搭配使用解决了循环引用问题

首先unique_ptr实现起来其实很简单,只需要对赋值函数(=运算符重载)和拷贝构造函数等这些利用对象创建新的对象的函数做出修改即可,当然析构函数部分的释放也是要注意的。

template<typename T>
class unique_ptr{
    T*ptr;
public:
    unique_ptr(T*p=nullptr):ptr(p){}
    unique_ptr(unique_ptr&&other) noexcept:ptr(other.release()){
        other.ptr=nullptr;
    }
    unique_ptr& operator=(unique_ptr&&other) noexcept{
        if(other.ptr!=this->ptr){
            change(other.release());
        }
        return *this;
    }
    T*get()const{
        return ptr;
    }
    T& operator*() const{
        return *ptr;
    }
    T* operator->() const{
        return ptr;
    }
    explicit operator bool() const{
        return ptr!=nullptr;
    }
    ~unique_ptr(){
        delete ptr;
        ptr=nullptr;
    }
    T*release(){
        T*tmp=this->ptr;
        this->ptr=nullptr;
        return tmp;
    }
    void change(T*cur=nullptr){
        if(cur!=ptr){
            delete ptr;
            ptr=cur;
        }
    }
    unique_ptr(const unique_ptr&)=delete;
    unique_ptr& operator=(const unique_ptr&)=delete;
};

然后看shared_ptr它的话要比unique_ptr要自由的多实现起来也很简单,只需要多一步计数操作即可。

template<typename T>
class shared_ptr{
    T*ptr;
    int* cnt;
public:
    shared_ptr(T*p=nullptr):ptr(p),cnt(new int(1)){}
    shared_ptr(const shared_ptr&other) noexcept:ptr(other.ptr),cnt(other.cnt){
        (*cnt)++;
    }
    shared_ptr& operator=(const shared_ptr&other) noexcept{
        if(this!=&other){
            release();
            this->ptr=other.ptr;
            this->cnt=other.cnt;
            (*cnt)++;
        }
        return *this;
    }
    T*get()const{
        return ptr;
    }
    T& operator*() const{
        return *ptr;
    }
    T* operator->() const{
        return ptr;
    }
    explicit operator bool() const{
        return ptr!=nullptr;
    }
    ~unique_ptr(){
        release();
    }
    void release(){
        if(cnt&&--(*cnt)==0){
            delete cnt;
            delete ptr;
        }
        ptr=nullptr;
        cnt=nullptr;
    }
};

但是注意shared_ptr的计数可能会带来一个问题:循环引用
看下面代码其实很好理解,就是循环指向造成计数到不了0从而释放不了对象。

class B;
class A{
public:
    shared_ptr<B>aptr;
};
class B{
public:
    shared_ptr<A>bptr;
};
int main(){
	shared_ptr<A>a=new A;
	shared_ptr<B>b=new B;
	a->aptr=b;
	b->bptr=a;
}

解决方式:引入了weak_ptr搭配shared_ptr使用,weak_ptr是对对象的一种弱引用,它不会增加对象的use_count,weak_ptr和shared_ptr可以相互转化,shared_ptr可以直接赋值给weak_ptr,weak_ptr也可以通过调用lock函数来获得shared_ptr。

  1. weak_ptr指针通常不单独使用,只能和 shared_ptr 类型指针搭配使用。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。
  2. weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
  3. 需要注意weak_ptr不维护资源的释放,因为需要避免资源被释放两次,因此weak_ptr常用在为生命周期一致的shared_ptr赋值
template <typename T>
class MySharedPtr;

template <typename T>
class MyWeakPtr;

template <typename T>
class MySharedPtr {
private:
    T* ptr;
    int* refCount;
    int* weakCount;

public:
    explicit MySharedPtr(T* p = nullptr) : ptr(p), refCount(new int(1)), weakCount(new int(0)) {}

    MySharedPtr(const MySharedPtr& other) : ptr(other.ptr), refCount(other.refCount), weakCount(other.weakCount) {
        (*refCount)++;
    }

    ~MySharedPtr() {
        release();
    }

    int use_count() const {
        return (refCount ? *refCount : 0);
    }

    T* get() const {
        return ptr;
    }

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

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

    MyWeakPtr<T> weak_ptr() {
        (*weakCount)++;
        return MyWeakPtr<T>(this->ptr, this->refCount, this->weakCount);
    }

    MySharedPtr& operator=(const MySharedPtr& other) {
        if (this != &other) {
            release();
            ptr = other.ptr;
            refCount = other.refCount;
            weakCount = other.weakCount;
            (*refCount)++;
        }
        return *this;
    }

private:
    void release() {
        if (refCount) {
            (*refCount)--;
            if (*refCount == 0) {
                delete ptr;
                delete refCount;
                if (*weakCount == 0) {
                    delete weakCount;
                }
            }
        }
    }

    friend class MyWeakPtr<T>;
};

template <typename T>
class MyWeakPtr {
private:
    T* ptr;
    int* refCount;
    int* weakCount;

public:
    MyWeakPtr() : ptr(nullptr), refCount(nullptr), weakCount(nullptr) {}

    MyWeakPtr(T* p, int* rc, int* wc) : ptr(p), refCount(rc), weakCount(wc) {}

    MyWeakPtr(const MyWeakPtr& other) : ptr(other.ptr), refCount(other.refCount), weakCount(other.weakCount) {
        (*weakCount)++;
    }

    ~MyWeakPtr() {
        release();
    }
    MySharedPtr<T> lock() {
        if (expired()) {
            return MySharedPtr<T>();
        }
        (*refCount)++;
        return MySharedPtr<T>(ptr, refCount, weakCount);
    }

    MyWeakPtr& operator=(const MyWeakPtr& other) {
        if (this != &other) {
            release();
            ptr = other.ptr;
            refCount = other.refCount;
            weakCount = other.weakCount;
            (*weakCount)++;
        }
        return *this;
    }
    bool expired() const {
        return (refCount == nullptr || *refCount == 0);
    }
private:
    void release() {
        if (weakCount) {
            (*weakCount)--;
            if (*weakCount == 0) {
                delete weakCount;
            }
        }
    }
};

shared_ptr和weak_ptr可以互相构造,看下面的代码就知道它们是如何解决循环引用的情况

class B;
class A{
public:
    MYShared_Ptr<B>aptr;
};
class B{
public:
    MYWeak_Ptr<A>bptr;
};
int main(){
	MYShared_Ptr<A>a=new A;
	MYShared_Ptr<B>b=new B;
	a->aptr=b.lock();
	b->bptr=a;
}

简单的来说就是通过两个可以互相转换的类型避免产生同种类型的环,对不同类型的环它们释放的时候互不影响对方的计数所以可以正常释放。


http://www.kler.cn/a/226698.html

相关文章:

  • 导出pdf 加密、加水印、加页脚
  • php工厂模式
  • yo!这里是单例模式相关介绍
  • Keil软件某些汉字输出乱码,0xFD问题,51单片机
  • Proteus仿真软件在单片机教学中的应用
  • 华为VRP系统简介
  • C++ 之LeetCode刷题记录(二十七)
  • 洛谷 P1803 凌乱的yyy / 线段覆盖
  • Security ❀ TCP异常报文详解
  • 测试工作(新入职)感悟
  • 帮管客CRM 文件上传漏洞
  • 嵌入式C语言学习——基于Linux与GCC
  • 创建型模式-单例模式:定义、实现及应用
  • STM32标准库+HAL库 | 输入捕获测量PWM的脉冲频率+占空比
  • 快速Diff算法-Vue3
  • C++ 日期类的实现
  • C++之std::tuple(一) : 使用
  • Entity实体设计
  • 【数据结构与算法】二叉树前序,中序,后序遍历非递归版。Leetcode接口
  • 基于 Echarts 的 Python 图表库:Pyecahrts交互式的日历图和3D柱状图