智能指针(附C++实现代码)


什么是智能指针

智能指针的本质是将指针封装为类成员对象,并在析构函数中删除指针指向的内存,主要的作用是对作用域内的动态分配内存对象进行自动施放,解决使用指针带来的内存泄漏问题。

1. auto_ptr

特点:这种类型的指针在进行拷贝构造的时候,会将自己的值赋值給另外一个指针,然后将自己指针的内容进行清空。

在这里插入图片描述

auto_ptr的使用

  1. 基本类型
    auto_ptr<int> ptr(new int(100));
    cout << *ptr << endl;
    *ptr = 20;
    cout << *ptr << endl;
    auto_ptr<int> ptr3(ptr);
    cout << *ptr3 << endl;
    // cout << *ptr << endl;   // 此时ptr已经被清空

运行结果为:
在这里插入图片描述

  1. 类类型
    auto_ptr<Simple>ptr2(new Simple);  // Simple是一个类
    ptr2->Func();   

类的定义如下:

class Simple{
public:
    void Func(){
        cout << "Simple::Func" << endl;
    }
};

运行结果为:
在这里插入图片描述

缺陷:

  1. 两个auto_ptr不能同时拥有同一个对象
  2. auto_ptr不能管理数组指针
  3. auto_ptr被拷贝或被赋值后,失去对原指针的管理.这种情况被称为指针所有权传递。
  4. auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。

    注意:不建议使用auto_ptr,目前使用较多的是unique_ptr和shared_ptr

auto_ptr的实现代码

class auto_ptr{
    T* ptr;
public:
    auto_ptr(T* ptr):ptr(ptr){}
    auto_ptr(const auto_ptr& p):ptr(p.ptr){
        p.ptr = nullptr;
    };
    auto_ptr& operator=(const auto_ptr& p){
        if(this == &p) return *this;
        ptr = p.ptr;
        p.ptr = nullptr;
        return *this;
    }
    ~auto_ptr(){
        delete ptr;
    }


    T& operator*(){ return *ptr; }
    T* operator->(){ return &ptr; }
    bool operator==(const auto_ptr& p)const{ return ptr == p.ptr;}
    bool operator!=(const auto_ptr& p)const{ return ptr != p.ptr;}
};

2. unique_ptr

特点:无法进行复制和赋值

注:复制: unique_ptr ptr1(ptr2) 赋值:ptr1 = ptr2

unique_ptr的用法

    unique_ptr<int>ptr4(new int(123));
    cout << *ptr4 << endl;
    *ptr4 = 200;
    cout << *ptr4 << endl;

    unique_ptr<Simple>ptr5(new Simple);
    ptr5->Func();

代码运行结果为:
在这里插入图片描述

unique_ptr的实现代码

template<class T>
class unique_ptr{
    T* ptr;
public:
    unique_ptr(T* ptr):ptr(ptr){}
    unique_ptr(const unique_ptr& p) = delete;
    unique_ptr& operator=(const unique_ptr& p) = delete;
    ~unique_ptr(){
        delete ptr;
    }
    T& operator*(){ return *ptr; }
    T* operator->(){ return &ptr; }
    bool operator==(const unique_ptr& p)const{ return ptr == p.ptr;}
    bool operator!=(const unique_ptr& p)const{ return ptr != p.ptr;}
};

3. shared_ptr

shared_ptr相比较unique_ptr可以进行赋值和复制

shared_ptr可以进行复制和赋值而不需要担心二次释放的问题的原因是,在该类型的指针中由一个计数器,每次赋值和复制的时候都会进行计数,只有当该计数器值为 0 的时候才会释放对应指向的空间。工作过程如图所示:

在这里插入图片描述

share_ptr的使用如下:

    shared_ptr<int> ptr7(new int(1000));
    cout << *ptr7 << endl;

    shared_ptr<int> ptr8(ptr7);
    cout << *ptr7 << endl;
    cout << *ptr8 << endl;

运行结果如下:
在这里插入图片描述

shared_ptr缺陷:

  • 当出现循环引用的时候,由于类对象的析构必须从类的成员开始,就会出现循环的情况,具体如下。

示例如下:

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;

class A {
public:  
    ~A() { cout <<"destroying A\n"; }
public:  
    shared_ptr<B> m_b;
};

class B {
public:  
    ~B() { cout <<"destroying B\n"; }
public:  
    shared_ptr<A> m_a;
};
int main(){  
    shared_ptr<A> a(new A());
    shared_ptr<B> b(new B);
    a->m_b = b;
    b->m_a = a;
}

运行这个程序,会发现该程序不会调用析构函数,这是因为对象a析构的时候,需要析构它的成员变量m_b,但是m_b进行析构的又需要调用类B的析构函数,类B进行析构的时候需要对它的成员变量进行析构,又需要调用类A的析构,这样就会形成循环,从而无法进行析构。

4. weak_ptr

weak_ptr就可以解决shared_ptr的循环引用的问题。

我们在shared_ptr遇到的循环引用问题就我们就可以使用wesk_ptr来是协助,我们将上述类B中的智能指针更换为weak_ptr类指针,具体代码如下:

#include <iostream>
#include <memory>
using namespace std;

class A;
class B;

class A {
public:
    ~A() { cout <<"destroying A\n"; }
public:
    shared_ptr<B> m_b;
};

class B {
public:
    ~B() { cout <<"destroying B\n"; }
public:
    weak_ptr<A> m_a;
};
int main(){
    shared_ptr<A> a(new A());
    shared_ptr<B> b(new B);
    a->m_b = b;
    b->m_a = a;
}

运行结果如下:

在这里插入图片描述
这时程序运行结束后可以成功进行析构。

weak_ptr的好处

  1. 打破递归的依赖关系
  2. 使用一个共享的资源但是不要所有权,不添加引用计数
  3. 避免悬空指针。

总结

注意:

  • 虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案。
  • 如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。

    因此,不要认为只要使用了智能指针便能杜绝内存泄漏。
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值