【C++】智能指针的定义与实现

一、什么是智能指针?

        智能指针是存储指向动态分配(堆)对象指针的类。除了能够在适当的时间自动删除指向的对象外,他们的工作机制很像C++的内置指针。智能指针在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象。他们也可以用于跟踪被多用户共享的动态分配对象。

二、智能指针的原理

        智能指针(smart pointer)的通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象的指针指向同一对象。每次创建类的新对象时,初始化指针就将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,析构函数减少引用计数(如果引用计数减至0,则删除基础对象)。

#include <iostream>
template<typename T>
class SmartPtr
{
public:
	SmartPtr(T* ptr) :mptr(ptr)
	{
		flag = true;
	}
	~SmartPtr()
	{
		if (flag)
		{
			delete mptr;
		}
		mptr = NULL;
	}
	/*
		旧的有释放权   ==》  新的就有
		旧的无释放权   ==》  新的无
		旧的赋后肯定没有释放权  
		this->flag = rhs.flag;
		rhs.flag = false;
	*/
	SmartPtr(const SmartPtr<T>& rhs)
	{
		this->mptr = rhs.mptr;

		this->flag = rhs.flag;
		rhs.flag = false;
	}
	SmartPtr<T>& operator=(const SmartPtr<T>& rhs)
	{
		if (this != &rhs)
		{
			if (flag)
			{
				delete mptr;
			}
			mptr = rhs.mptr;
			this->flag = rhs.flag;
			rhs.flag = false;
		}
		return *this;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	T* mptr;
	mutable bool flag;//是否拥有释放权   true  有   false 无 
};
void Fun(SmartPtr<int> psp)
{
	
}
int main()
{
	SmartPtr<int> sp1 = new int;
	SmartPtr<int> sp2 = sp1;
	Fun(sp2);//错误
	*sp1 = 20;
	return 0;
}

 

三、作用以及优缺点

1、设计简单、效率高。

2、防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存。另外指针的释放时机也是非常有考究的,多次释放同一个指针会造成程序崩溃,这些都可以通过智能指针来解决。

方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存。

四、C++标准库中的智能指针

智能指针在C++11后提供,有:unique_ptr,shared_ptr,weak_ptr  都在boost库中。

C++11前,auto_ptr:一个堆内存只有一块智能指针拥有它的所有权。

旧的有释放权===》新的就有;

旧的没有释放权===》新的就没有;

无论旧的有无,赋值后都没有。

1、auto_ptr的实现

所有权唯一

            旧智能指针对象赋给新智能指针对象
            取消旧智能指针的所有权  
        缺点:
            所有权转移导致旧智能指针提前失效 

#include <iostream>
template<typename T>
class Auto_Ptr
{
public:
	Auto_Ptr(T* ptr) :mptr(ptr){}
	Auto_Ptr(const Auto_Ptr<T>& rhs)
	{
		mptr = rhs.mptr;
		rhs.Release();
	}
	Auto_Ptr<T>& operator=(const Auto_Ptr<T>& rhs)
	{
		if (this != &rhs)
		{
			delete mptr;//delete NULL;
			mptr = rhs.mptr;
			rhs.Release();
		}
		return *this;
	}
	~Auto_Ptr()
	{
		if (mptr != NULL)
		{
			delete mptr;
		}
		mptr = NULL;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	void Release()const
	{
		(T*)mptr = NULL;
	}
	T* mptr;
};
int main()
{
	Auto_Ptr<int> ap1 = new int;
	Auto_Ptr<int> ap2 = ap1;

	*ap1 = 20;
	return 0;
}

2、shared_ptr的实现

带有引用计数的智能指针(强智能指针:引用计数都会加)

引用计数:有多少个智能指针对象管理这个堆内存

shared_ptr:多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是=

        1、避免循环引用,shared_ptr的一个最大的陷阱是循环引用,循环,循环引用会导致堆内存无法正确释放,导致内存泄漏。

        2、一个原始指针不能同时初始化多个shared_ptr,否则会造成二次释放同一内存。

        3、初始化时应该注意:智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。

        4、拷贝和赋值时要注意:拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。后来指向的对象引用计数加1,指向后来的对象

#include<iostream>
#include <memory>
/*
	auto_ptr
		一块堆内存 只要有一个智能指针拥有所有权  

*/
/*
	shared_ptr  #include<memory>
	weak_ptr
*/
class Ref_Management
{
private:
	Ref_Management() :index(0){}
	Ref_Management(const Ref_Management&);
public:
	static Ref_Management* getInstance()
	{
		return &rm;
	}
public:
	void addRef(void* ptr)
	{
		int idx = FindAddr(ptr);
		if (idx < 0)
		{
			//node[index].ptr = ptr;
			//node[index++].ref = 1;
			Node tmp(ptr, 1);
			node[index++] = tmp;
		}
		else
		{
			node[idx].ref++;
		}
	}
	void delRef(void* ptr)
	{
		int idx = FindAddr(ptr);
		if (idx < 0)
		{
			throw std::exception("addr is not exist!");
		}
		else
		{
			if (node[idx].ref != 0)
			{
				node[idx].ref--;
			}
		}
	}
	int getRef(void* ptr)
	{
		int idx = FindAddr(ptr);
		if (idx < 0)
		{
			throw std::exception("addr is not exist!");
		}
		return node[idx].ref;
	}
private:
	int FindAddr(void* ptr)
	{
		int rt = -1;
		for (int i = 0; i < 10; i++)
		{
			if (node[i].ptr == ptr)
			{
				rt = i;
				break;
			}
		}
		return rt;
	}
	class Node
	{
	public:
		Node(void* p = NULL, int r = 0) :ptr(p), ref(r){}
	public:
		void* ptr;//堆内存的地址
		int ref;  //对应的引用计数
	};
	Node node[10];
	int index;//当前有效个数,当前可以插入的数组有效下标
	static Ref_Management rm;
};
Ref_Management Ref_Management::rm;
template<typename T>
class Shared_Ptr
{
public:
	Shared_Ptr(T* ptr) :mptr(ptr)
	{
		prm->addRef(mptr);
	}
	Shared_Ptr(const Shared_Ptr<T>& rhs) :mptr(rhs.mptr)
	{
		prm->addRef(mptr);
	}
	Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
	{
		if (this != &rhs)//判字符
		{
			prm->delRef(mptr);//引用计数减
			if (prm->getRef(mptr) == 0)//判断引用计数是否等于0
			{
				delete mptr;//减为0 ,则释放
			}
			mptr = rhs.mptr;
			prm->addRef(mptr);
		}
		return *this;
	}
	~Shared_Ptr()
	{
		prm->delRef(mptr);
		if (prm->getRef(mptr) == 0)
		{
			delete mptr;
		}
		mptr = NULL;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	T* mptr;
	static Ref_Management* prm;
};
template<typename T>
Ref_Management* Shared_Ptr<T>::prm = Ref_Management::getInstance();

int main()
{
	int* p = new int;
	Shared_Ptr<int> sp1(p);//sp1.mptr
	Shared_Ptr<int> sp2(p);
	Shared_Ptr<int> sp3(p);
	return 0;
}

3、Scorp_ptr的实现

一个智能指针只能引用一块内存

不允许权限转移

#include <iostream>
template<typename T>
class Scope_Ptr
{
public:
	Scope_Ptr(T* ptr) :mptr(ptr){}
	~Scope_Ptr()
	{
		delete mptr;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	Scope_Ptr(const Scope_Ptr<T>&);
	Scope_Ptr<T>& operator=(const Scope_Ptr<T>&);
	T* mptr;
};

int main()
{
	int* p = new int;
	Scope_Ptr<int> sp1(p);
	Scope_Ptr<int> sp2(p);
	Scope_Ptr<int> sp3(p);
	//Scope_Ptr<int> sp1 = new int;
	//Scope_Ptr<int> sp2 = sp1;
	return 0;
}

4、weak_ptr (解决强智能指针遗留的问题(相互引用(造成内存泄漏)))

弱智能指针:只做管理,其他什么都不做。

         1、不加引用计数

         2、不释放内存

不能单独使用,必须结合强智能指针使用。(配合shared_ptr使用)

        weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。

 

#include <iostream>
#include <memory>
class Ref_Management
{
private:
	Ref_Management() :index(0){}
	Ref_Management(const Ref_Management&);
public:
	static Ref_Management* getInstance()
	{
		return &rm;
	}
public:
	void addRef(void* ptr)
	{
		int idx = FindAddr(ptr);
		if (idx < 0)
		{
			//node[index].ptr = ptr;
			//node[index++].ref = 1;
			Node tmp(ptr, 1);
			node[index++] = tmp;
		}
		else
		{
			node[idx].ref++;
		}
	}
	void delRef(void* ptr)
	{
		int idx = FindAddr(ptr);
		if (idx < 0)
		{
			throw std::exception("addr is not exist!");
		}
		else
		{
			if (node[idx].ref != 0)
			{
				node[idx].ref--;
			}
		}
	}
	int getRef(void* ptr)
	{
		int idx = FindAddr(ptr);
		if (idx < 0)
		{
			throw std::exception("addr is not exist!");
		}
		return node[idx].ref;
	}
private:
	int FindAddr(void* ptr)
	{
		int rt = -1;
		for (int i = 0; i < 10; i++)
		{
			if (node[i].ptr == ptr)
			{
				rt = i;
				break;
			}
		}
		return rt;
	}
	class Node
	{
	public:
		Node(void* p = NULL, int r = 0) :ptr(p), ref(r){}
	public:
		void* ptr;//堆内存的地址
		int ref;  //对应的引用计数
	};
	Node node[10];
	int index;
	static Ref_Management rm;
};
Ref_Management Ref_Management::rm;
template<typename T>
class Shared_Ptr
{
public:
	Shared_Ptr(T* ptr = NULL) :mptr(ptr)
	{
		prm->addRef(mptr);
	}
	Shared_Ptr(const Shared_Ptr<T>& rhs) :mptr(rhs.mptr)
	{
		prm->addRef(mptr);
	}
	Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
	{
		if (this != &rhs)
		{
			prm->delRef(mptr);
			if (prm->getRef(mptr) == 0)
			{
				delete mptr;
			}
			mptr = rhs.mptr;
			prm->addRef(mptr);
		}
		return *this;
	}
	~Shared_Ptr()
	{
		prm->delRef(mptr);
		if (prm->getRef(mptr) == 0)
		{
			delete mptr;
		}
		mptr = NULL;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
	T* getPtr()const
	{
		return mptr;
	}
private:
	T* mptr;
	static Ref_Management* prm;
};
template<typename T>
Ref_Management* Shared_Ptr<T>::prm = Ref_Management::getInstance();

template<typename T>
class Weak_Ptr//弱智能指针的实现
{
public:
	Weak_Ptr(T* ptr = NULL) :mptr(ptr){}
	Weak_Ptr(const Weak_Ptr<T>& rhs) :mptr(rhs.mptr){}
	Weak_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
	{
		mptr = rhs.getPtr();
		return *this;
	}
	~Weak_Ptr(){}
private:
	T* mptr;
};
class B;
class A
{
public:
	A()
	{
		std::cout << "A()" << std::endl;
	}
	~A()
	{
		std::cout << "~A()" << std::endl;
	}
public:
	Weak_Ptr<B> spa;
};
class B
{
public:
	B()
	{
		std::cout << "B()" << std::endl;
	}
	~B()
	{
		std::cout << "~B()" << std::endl;
	}
public:
	Weak_Ptr<A> spb;
};

int main()
{
	Shared_Ptr<A> pa(new A());
	Shared_Ptr<B> pb(new B());
	pa->spa = pb;
	pb->spb = pa;
	return 0;
}

5、unique_ptr

        unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。相比与原始指针unique_ptr用于其RAII的特性,使得在出现异常的情况下,动态资源能得到释放。

        unique_ptr指针本身的生命周期从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。

 

 

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值