C++ 智能指针的实现

1. shared_ptr 的特性

shared_ptr内部维护一个引用计数,当创建、copy、销毁时,引用计数都会变化

拷贝构造函数 p(q),会递增q的引用,pq指向同一个对象

 shared_ptr<string> q = make_shared<string>("smartptr");
shared_ptr<string> p(q); //递增q的引用计数,p和q指向同一个对象
std::cout << "p use_count=" << p.use_count() << std::endl; // 2
std::cout << "q use_count=" << q.use_count() << std::endl; // 2 

赋值构造函数 p=q,会递增q的引用,递减p的引用

 class Man
{
public:
 Man(string name) :_name(name) { std::cout << _name << ": Create" << std::endl; };
 ~Man() { std::cout << _name << ": Delete" << std::endl; };
private:
 string _name;
};

shared_ptr<Man> q = make_shared<Man>("Man_q");
shared_ptr<Man> p = make_shared<Man>("Man_p");
std::cout << "p use_count=" << p.use_count() << std::endl; // 1
std::cout << "q use_count=" << q.use_count() << std::endl; // 1

p = q; //递增q的引用计数,递减p的引用,当p引用为0时,释放其内部的对象。p和q指向同一个对象
std::cout << "p use_count=" << p.use_count() << std::endl; // 2
std::cout << "q use_count=" << q.use_count() << std::endl; // 2

上面这个示例代码,当 执行 p = q时,

(1) 递减p的引用计数,p之前的引用计数为1,递减后为0

(2) 如果p的引用计数为0,则释放p中原有的对象,此时会调用Man的析构,打印”Man_p :Delete”

(3) 递增q的引用计数,即变为2

(4) 将 p 指向 q

2. 实现一个智能指针SmartPtr

理解了上面的过程之后,就可以动手实现一个智能指针

 template <class _Ty>
class SmartPtr
{
public:
	SmartPtr(_Ty* ptr) :_refptr(new RefPtr(ptr)) {};
	SmartPtr(const SmartPtr& obj)
	{
		if (this != &obj)
		{
			if (_refptr)
			{
				*(_refptr->_count) -= 1;
				if (*(_refptr->_count) == 0)
				{
					delete _refptr;
				}
			}

			_refptr = obj._refptr;
			*(_refptr->_count) += 1;
		}	
	}

	SmartPtr& operator=(const SmartPtr& obj)
	{
		if (this != &obj)
		{
			if (_refptr)
			{
				*(_refptr->_count) -= 1;
				if (*(_refptr->_count) == 0)
				{
					delete _refptr;
				}
			}

			_refptr = obj._refptr;
			*(_refptr->_count) += 1;
		}
		return *this;
	}

	~SmartPtr()
	{
		*(_refptr->_count) -= 1;
		if (*(_refptr->_count) == 0)
		{
			delete _refptr;
		}
	}

	int use_count() { return *(_refptr->_count); }

private:
	class RefPtr                   //辅助类,用于保存引用计数 和 实际对象
	{                              //  之所以需要使用辅助类,而不是直接用int _count和 _Ty* 成员变量    
		friend class SmartPtr;     //  是为了能否封装住 _count和_Ty* 这些变量,对外不可见
		RefPtr(_Ty* ptr) :_ptr(ptr) { _count = new int(1);  };
		~RefPtr() { if (_ptr) delete _ptr; delete _count; };
		int* _count;
		_Ty* _ptr;
	};
	RefPtr* _refptr;
};

这里有几个细节
(1) 将SmartPtr声明为RefPtr的友元之后,SmartPtr就可以访问RefPtr的私有变量
(2) RefPtr全部为private,可以很好的封装不对外可见
(3) _count 声明为int* 指针,这样,在拷贝构造和赋值构造中,就可以对 const SmartPtr& obj 这个obj对象的count也进行改变,
因为this->_refptr->_count和 obj的count指向同个内存区;
再者对this->_refptr->_count 做递减时,也会影响到其他指向this的智能指针的引用计数

使用示例如下

 SmartPtr<Man> man1(new Man("Man1"));
{
	std::cout << "Cnt1:" << man1.use_count() << std::endl; // 1

	SmartPtr<Man> man2(man1);  //man2 是man1的copy
	std::cout << "Cnt1:" << man1.use_count() << std::endl;  // 2
	std::cout << "Cnt2:" << man2.use_count() << std::endl;  // 2

	{
		SmartPtr<Man> man3(new Man("Man3"));
		std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2
		std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2
		std::cout << "Cnt3:" << man3.use_count() << std::endl; // 1

		man3 = man1; //man3先析构了Man3, 再指向了man1, 同时引用+1 ; Man3: Delete
		std::cout << "Cnt1:" << man1.use_count() << std::endl; // 3
		std::cout << "Cnt2:" << man2.use_count() << std::endl; // 3
		std::cout << "Cnt3:" << man3.use_count() << std::endl; // 3
	}

	std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2 因为man3 出了作用域,析构了
	std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2

}
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 1 因为man2 出了作用域,析构了

输出如下
Man1: Create
Cnt1:1
Cnt1:2
Cnt2:2
Man3: Create
Cnt1:2
Cnt2:2
Cnt3:1
Man3: Delete
Cnt1:3
Cnt2:3
Cnt3:3
Cnt1:2
Cnt2:2
Cnt1:1

作者:JarvisChu
原文链接:C++ 智能指针的实现
版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0

发表评论