模拟实现boost库里的智能指针

本文介绍了智能指针的概念,探讨了为何需要封装智能指针以避免内存泄漏。模拟实现了Boost库中的Auto_ptr和ScopedPtr,讨论了它们的实现方式和优缺点。同时,提到了ScopedPtr的防拷贝策略以及shared_ptr的线程安全和循环引用问题,建议在实际使用中优先考虑ScopedPtr。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 智能指针
    什么是智能指针呢,它是行为类似于指针的类对象,但这种对象还有其他功能。我们为什么要封装智能指针类对象呢?这是因为C++中的动态内存需要用户自己来维护,动态开辟的空间,在出函数作用域或者程序正常退出前必须释放掉,否则会造成内存泄漏,所以我们会定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
    接下来我们模拟实现以下boost库里的一些智能指针了解他们的使用。

  • Auto_ptr
    第一种实现方法

#include<iostream>
#include<stdlib.h>
using namespace std;
//模拟实现auto_ptr,非常坑爹什么情况下都不要使用
template <typename T>
class Auto_ptr
{
public:
    Auto_ptr(T *ptr = NULL)
        :_ptr(ptr)
    {}
    Auto_ptr(Auto_ptr &a)//拷贝构造函数
    {
        _ptr = a._ptr;
        a._ptr = NULL;//有新的指针使用这块空间为了避免程序崩溃原先的指针
        //这快空间脱离关系
    }
    Auto_ptr & operator=(Auto_ptr& a)
    {
        if(this != &a)//防止自身赋值
        {
            if(NULL !=_ptr)
            {
                delete _ptr;
                _ptr = NULL;
            }
            _ptr = a._ptr;
            a._ptr = NULL;
        }
        return *this;
    }
    ~Auto_ptr()
    {
        if(NULL != _ptr)
        {
            delete _ptr;
            _ptr = NULL;
            cout<<"_ptr has been deleted";
        }

    }
private:
    T *_ptr;
};

第二种实现方法-添加bool变量

template <typename T>
class Auto_ptr
{
public:
    Auto_ptr(T *ptr = NULL)
        :_ptr(ptr)
        ,Onlyone(true)
    {
        if(NULL == ptr)
        {
            Onlyone = false;
        }
    }
    Auto_ptr(Auto_ptr &a)//拷贝构造函数
    {
        _ptr = a._ptr;
        Onlyone = true;
        a.Onlyone = false;//有新的指针使用这块空间为了避免程序崩溃原先管理空间的指针Onlyone置为false

    }
    Auto_ptr & operator=(Auto_ptr& a)
    {
        if(this != &a)//防止自身赋值
        {
            if(false != Onlyone)
            {
                delete _ptr;
                _ptr = NULL;
            }
            _ptr = a._ptr;
            a.Onlyone = false;
            Onlyone = true;
        }
        return *this;
    }
    ~Auto_ptr()
    {
        if(true == Onlyone)
        {
            delete _ptr;
            _ptr = NULL;
            cout<<"_ptr has been deleted";
        }

    }
private:
    T *_ptr;
    bool Onlyone;
};

两种方法各有优缺点,这里我们来看一下第二种的Bug.

void FunTest()
{
    Auto_ptr<int>ap1(new int)
    if(true)
    {
        Auto_ptr<int>ap2(new int)
    }//出了if作用域空间就会被释放,此时使用ap1指针就会出现问题
}
  • ScopedPtr
    ScopedPtr这个智能指针采取比较暴力的手段,让空间只由自己一个来管理,我们来看看具体的实现
#define  _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
template <typename T>
class ScopedPtr
{
public:
    ScopedPtr(T *p = NULL)
        :_p(p)
    {}
    ~ScopedPtr()
    {
        if(NULL != _p)
        {
            delete _p;
            _p = NULL;
        }
    }
private:
    //为了防止浅拷贝的问题出现,使这个类无法被拷贝和赋值
    //采用的方法就是将拷贝构造函数和赋值运算符重载函数
    //访问权限设为私有,并且只给出声明。
    ScopedPtr(const ScopedPtr& s);
    T &operator=(const ScopedPtr& s);
private:
    T *_p;
};
void FunTest()
{
    ScopedPtr<int> sp(new int);
    //ScopedPtr<int> sp1(sp);//错误无法完成拷贝
    ScopedPtr<int> sp1(new int);
    //sp1 = sp;//无法成功赋值
}
int main()
{
    FunTest();
    system("pause");
    return 0;
}

一个类如何防拷贝呢?
1.声明为私有的:这样可以通过友元函数和成员函数拷贝成成功(不可取)
2.声明为公有:可能在类外被定义(不可取)
3.声明为私有(只给出声明)(可以实现)

  • SharedPtr

最后我们来模拟boost库中shared_ptr,但这个函数线程不是很安全,一般建议使用scoped_ptr

#define  _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
//共享的智能指针
//使用引用计数
template <typename T>
struct Delete
{
    void operator()(T* ptr)
    {
        if(NULL != ptr)
        {
            delete ptr;
            ptr = NULL;
        }
    }
};
struct Fclose
{
    void operator()(FILE* ptr)
    {
        if(NULL != ptr)
        {
            fclose(ptr);
        }
    }
};
template <typename T,typename _Del = Delete<T>>
class SharedPtr
{
public:
    SharedPtr(T *p = NULL,_Del del = Delete<T>())//构造函数
        :_p(p)
        ,_pCount(NULL)//不可以在此处初始化为1,如果p为NULL
        ,_del(del)
    {
        if(NULL != _p)
        {
            _pCount = new int(1);
        }
    }

    SharedPtr(const SharedPtr& sp)//拷贝构造函数
        :_p(sp._p)
        ,_pCount(sp._pCount)
    {
        if(NULL != _pCount)//注意_pCount为NULL的情况,此时不能解引用,不用++
        {
            ++(*_pCount);
        }
    }
    SharedPtr& operator=(const SharedPtr& sp)
    {
        if(_p != sp._p)//注意自身赋值,有时候两个不同的对象但里面的的指针指向
            //相同不能用(this != &sp)判断出来
        {
            if(NULL != _pCount)//被复制的对象本身为空
            {
                if(0 == --(*_pCount))//被赋值的对象自己管理一段空间,需要释放
                {
                    Release();
                }
            }
                _p = sp._p;//和其他对象共同管理
                _pCount = sp._pCount;
                if(NULL != sp._pCount)//注意判断赋值对象是否为空
                {
                    ++(*_pCount);
                }
            }
        return *this;
    }

    ~SharedPtr()
    {
        Release();
    }
private:
    void Release()
    {
        if(0 == --(*_pCount))
        {
            _del(_p);
            delete _pCount;//记得要释放引用计数的空间
            _pCount = NULL;
        }
    }
private:
    T *_p;
    int *_pCount;
    _Del _del;
};

SharedPtr存在线程安全问题,并且也存在循环引用的问题,我们之后会详细讨论,所以建议使用ScopedPtr。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值