【C++】C++复习----解析智能指针

智能指针

智能指针是为了方便管理动态内存,防止产生内存泄漏,而对指针进行的一层封装,可以通过析构函数,构造函数,赋值运算符重载的处理,而实现的可以自动管理指针所指向的动态内存,防止产生内存泄漏。

资源分配即初始化(RAII):定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。

之前的版本中有 auto_ptr,在C++11版本之后提供了shared_ptr、unique_ptr、weak_ptr,包含在头文件< memory>中。


1、auto_ptr (浅拷贝)

1.1 模拟实现
#pragma once
#include<iostream>
using namespace std;

template<class T>
class Auto_ptr
{
public:
    //构造函数
    Auto_ptr(T* p)
        :_ptr(p)
    {}

    //拷贝构造函数--管理权的转移--只改变指针的指向(浅拷贝)
    Auto_ptr(Auto_ptr<T>& ap){
        _ptr = ap._ptr;
        ap._ptr = NULL;
    }

    //赋值运算符重载
    Auto_ptr<T>& operator=(const Auto_ptr<T>& ap){
        //防止自己给自己赋值
        if (*this != *ap){
            //释放旧空间
            if (_ptr){
                delete _ptr;
            }
            _ptr = ap._ptr;
            ap._ptr = NULL;
        }
        return *this;
    }

    T& operator*(){
        return *_ptr;
    }

    T* operator->(){
        return _ptr;
    }

    //析构函数
    ~Auto_ptr<T>(){
        if (_ptr)
            delete _ptr;
    }


private:
    T* _ptr;

};
1.2 auto_ptr缺陷

同一空间不能由两个及以上auto_ptr管理,会导致以下问题:

  1. 调用多次析构函数来释放同一内存空间,引起程序崩溃。
  2. 如果第二个变量的生命周期先到头,调用析构函数释放内存空间,那么第一个变量就会变成野指针。

这里写图片描述

auto_ptr的拷贝构造,将源指针的管理权交给目标指针,会使得源指针悬空,对原指针解引用导致很多问题:

这里写图片描述

auto_ptr不能用来管理数组,析构函数中用的是delete


2、scoped_ptr

scoped_ptr其实是对auto_ptr的改进,禁止拷贝,即一个空间只能被一个对象管理:

  1. 拷贝构造函数和赋值运算符重载函数只声明不实现
  2. 用private对其进行访问限定,防止在类外定义
2.1 模拟实现
#pragma once
#include<iostream>

using namespace std;

template<class T>
class Scoped_ptr{
public:
    //构造函数
    Scoped_ptr(T* p){
        _ptr = p;
    }
    T& operator*(){
        return *_ptr;
    }

    T* operator->(){
        return _ptr;
    }

    //析构函数
    ~Scoped_ptr<T>(){
        if (_ptr)
            delete _ptr;
    }

private:
    Scoped_ptr(const Scoped_ptr<int>&sp);//拷贝构造
    Scoped_ptr& operator=(const Scoped_ptr<int>&sp);//赋值运算符的重载
    T* _ptr;
};
2.2 缺陷

一个空间只能被一个对象管理,使用不方便

【注】:有兴趣可以定制一个删除器(使用函数指针或者仿函数的形式实现),来智能管理delete,free,fclose等。


3、scoped_array

scoped_array 和 scoped_ptr的功能是一样的,只是scoped_array管理的对象是数组,需要重载[]的形式。

3.1 模拟实现
#pragma once
#include<iostream>
using namespace std;

template<class T>
class Scoped_array{
public:
    //构造函数
    Scoped_array(T* p){
        _ptr = p;
    }

    T& operator[](size_t i){
        return _ptr[i];
    }

    //析构函数
    ~Scoped_array<T>(){
        if (_ptr)
            delete[] _ptr;
    }


private:
    //拷贝构造
    Scoped_array(const Scoped_array<int>& s);
    //赋值运算符的重载
    Scoped_array& operator=(const Scoped_array<int>& s);
    T* _ptr;
};

4、share_ptr

加入引用计数,防止出现多次析构同一空间的情况。在这里的引用计数要在堆上创建,否则出了作用域该引用计数就会被销毁了。

每创建一个share_ptr对象,给该对象模型的引用计数加一,用来标记此时这个空间有多少个指针指向。

在每次构造函数里,对前四个字节里的值,加一;

在析构函数里,先最前四个字节里的值减一,再判断前四个字节里的值是否为0,若为0,则释放内存空间。

在拷贝构造函数里,则需要考虑以下几种情况:

【情况一】

这里写图片描述

【情况二】

这里写图片描述

4.1 模拟实现
#pragma once
#include<iostream>

using namespace std;


template<class T>
class Share_ptr
{
public:
    //构造函数
    Share_ptr(const T* p)
        :_ptr(p)
        , _pCount(new int(1))
    {}

    Share_ptr(Share_ptr<T>* sp, int* spCount)
        :_ptr(sp)
        , _pCount(spCount)
    {}

    //拷贝构造函数
    Share_ptr(const Share_ptr<T>& sp){
        _ptr = sp._ptr;
        _pCount = sp._pCount;
        ++(*_pCount);
    }

    //赋值运算符重载
    Share_ptr& operator=(Share_ptr<T>& sp){
        if (this != &sp){
            if (--(*_pCount) == 0){
                delete _ptr;
                delete _pCount;
            }
            _ptr = sp._ptr;
            _pCount = sp._pCount;
            ++(*_pCount);
        }
        return *this;
    }

    T&operator*(){
        return *_ptr;
    }

    T*operator->(){
        return _ptr;
    }

    ~Share_ptr(){
        if (--(*_pCount) == 0){
            delete _ptr;
            delete _pCount;
        }
    }

private:
    T* _ptr;
    int* _pCount;

};
4.2 缺陷
  1. 线程安全问题,改进措施:加上删除器,实现安全代码;
  2. 循环引用的问题,可能会导致内存泄漏;

循环引用:

这里写图片描述


5、weak_ptr(弱指针–>解决循环引用问题)??

弱指针:弱指针是指当指针指向原来空间时,引用计数不在进行加1

【注】:不是管理和释放当前的指针,而是避免了一次引用计数

模拟实现
#pragma once
#include<iostream>

using namespace std;

template<class T>
class Weak_ptr;


template<class T>
class Share_ptr
{
    friend class Weak_ptr<T>;
public:
    //构造函数
    Share_ptr(const T* p)
        :_ptr(p)
        , _pCount(new int(1))
    {}

    Share_ptr(Share_ptr<T>* sp, int* spCount)
        :_ptr(sp)
        , _pCount(spCount)
    {}

    //拷贝构造函数
    Share_ptr(const Share_ptr<T>& sp){
        _ptr = sp._ptr;
        _pCount = sp._pCount;
        ++(*_pCount);
    }

    //赋值运算符重载
    Share_ptr& operator=(Share_ptr<T>& sp){
        if (this != &sp){
            if (--(*_pCount) == 0){
                delete _ptr;
                delete _pCount;
            }
            _ptr = sp._ptr;
            _pCount = sp._pCount;
            ++(*_pCount);
        }
        return *this;
    }

    T&operator*(){
        return *_ptr;
    }

    T*operator->(){
        return _ptr;
    }

    ~Share_ptr(){
        if (--(*_pCount) == 0){
            delete _ptr;
            delete _pCount;
        }
    }

private:
    T* _ptr;
    int* _pCount;

};

template<class T>
class Weak_ptr
{
public:
    //可以将sharedptr的对像传给他,匿名对象
    Weak_ptr(Share_ptr<T>& sp){
        //这里将sharedptr的对象给weakptr(弱指针)进行初始化,此时弱指针指向的是sharedptr的对象
        _ptr = sp._ptr;
    }

    Weak_ptr()
        :_ptr(NULL)
    {}

    Weak_ptr& operator=(Share_ptr<T>& sp){
        _ptr = sp._ptr;
        return *this;
    }

    T& operator*(){
        return *_ptr;
    }

    T* operator->(){
        return _ptr;
    }

private:
    T* _ptr;
};

/*****************************使用实例*******************************/
struct ListNode
{
public:
    int _data;
    Weak_ptr<ListNode> _next;
    Weak_ptr<ListNode> _prev;
    ~ListNode(){
        cout << "delete ListNode" << endl;
    }
};

void TestSharePtrCycle()
{
    Share_ptr<ListNode> cur = new ListNode;//使用弱指针
    Share_ptr<ListNode> next = new ListNode;
    cur->_next = next;//weakptr接收的是sharedptr的对象
    next->_prev = cur;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值