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

xiaoxiao2021-02-28  147

智能指针 什么是智能指针呢,它是行为类似于指针的类对象,但这种对象还有其他功能。我们为什么要封装智能指针类对象呢?这是因为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。

转载请注明原文地址: https://www.6miu.com/read-60512.html

最新回复(0)