C++智能指针的实现

简介:

说起智能指针,不少人都不陌生。比方auto_ptr、shared_ptr、unique_ptr、weak_ptr。

依据shared_ptr的功能,自己仿造也实现了个。


对于shared_ptr这样的智能指针,有一个共享的引用计数器来控制指针对象的销毁,当引用计数器变为0时。则销毁指针指向的对象。对于多线程安全问题,我在代码中使用的Interlocked系列的原子操作函数。


在学习过程中,渐渐学会了RAII(Resource Acquisition Is Initialization),慢慢领略到了这样的模式的优点。

直接上代码:

SmartPtr.hpp

#pragma once
#include "stdafx.h"
#include <assert.h>
#include <windows.h>

//#define DEBUG_SMARTPTR

template<typename T>
class SmartPtr;

template <typename T>
class RefPtr
{
	friend class SmartPtr<T>;
	explicit RefPtr(T *p) :pointer(p), nUse(0)
	{	
		assert(pointer);
#ifdef DEBUG_SMARTPTR
		std::cout << "Create Pointer!" << std::endl;
#endif
	}

	RefPtr(const RefPtr&)
	{

	}

	RefPtr& operator= (const RefPtr & ref)
	{

	}

	~RefPtr()
	{
#ifdef DEBUG_SMARTPTR
		std::cout << "Delete Pointer!" << std::endl;
#endif
		assert(pointer);
		if (pointer != NULL)
		{
			delete pointer;
			pointer = NULL;
		}	
	}

	unsigned int AddRefCount()
	{
		return InterlockedIncrement((unsigned int*)&nUse);
	}

	unsigned int SubRefCount()
	{
		return InterlockedDecrement((unsigned int*)&nUse);
	}

	bool AddRefCount_lock()
	{
		for (;;)
		{
			unsigned int temp = nUse;
			if (temp == 0)
			{
				return false;
			}
			if (InterlockedCompareExchange((unsigned int *)&nUse, temp + 1, temp) == temp)
			{
				return true;
			}
		}		
	}

	volatile unsigned int nUse;
	T *pointer;
};

template<typename T>
class SmartPtr
{
public:
	explicit SmartPtr(T *pointer) :ptr(new RefPtr<T>(pointer))
	{
		assert(pointer);
#ifdef DEBUG_SMARTPTR
		std::cout << "Create SmartPointer!" << std::endl;
#endif
		ptr->AddRefCount();
	}

	explicit SmartPtr(const SmartPtr<T>& sp) :ptr(sp.ptr)
	{
#ifdef DEBUG_SMARTPTR
		std::cout << "Copy0 SmartPointer!" << std::endl;
#endif
		ptr->AddRefCount();
	}

	explicit SmartPtr(const SmartPtr<T>* sp) :ptr(sp->ptr)
	{
#ifdef DEBUG_SMARTPTR
		std::cout << "Copy1 SmartPointer!" << std::endl;
#endif
		ptr->AddRefCount();
	}

	SmartPtr& operator=(const SmartPtr<T>& sp)
	{
		if (sp.ptr != ptr)
		{
			//注意先加后减,防止指向同对象析构的问题
			if (sp.ptr->AddRefCount_lock())
			{
				if (ptr->SubRefCount() == 0)
				{
					delete ptr;
				}
				ptr = sp.ptr;
			}
		}
#ifdef DEBUG_SMARTPTR
		std::cout << "Copy2 SmartPointer!" << std::endl;
#endif
		return *this;
	}

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

	T* operator->() const
	{
		return GetPtr();
	}

	T& operator*()
	{
		return *ptr->pointer;
	}

	T& operator*() const
	{
		return *ptr->pointer;
	}

	bool operator!()
	{
		return !ptr;
	}

	~SmartPtr()
	{
		if (ptr->SubRefCount() == 0)
		{
			delete ptr;
		}
#ifdef DEBUG_SMARTPTR
		std::cout << "Delete SmartPointer!" << std::endl;
#endif
	}

	int GetRefCount() const
	{
		return ptr->nUse;
	}

	bool isNull()
	{
		return ptr->pointer == NULL;
	}

	T* GetPtr() const
	{
		assert(ptr->pointer);
		return ptr->pointer;
	}

	//返回对象
	T GetValue() const
	{
		assert(ptr->pointer);
		return *ptr->pointer;
	}

private:
	RefPtr<T> *ptr;
};

//兼容const比較
template<typename T>
inline bool operator==(const SmartPtr<T>& a,const SmartPtr<T>& b)
{
	return a.GetPtr() == b.GetPtr();
}
template<typename T>
inline bool operator!=(const SmartPtr<T>& a,const SmartPtr<T>& b)
{
	return a.GetPtr() != b.GetPtr();
}
test.cpp

#include "SmartPtr.hpp"
#include <iostream>
#include <process.h>


#define THREADCOUNT 10

typedef struct _PERSON_
{
	char szName[20];
	int nAge;
	~_PERSON_()
	{
		std::cout << "Person Distructor!"<< std::endl;
	}
}PERSON;

SmartPtr<PERSON> g_p(new PERSON{ "g_test", 12 });

unsigned int __stdcall testThread(void *pParam)
{
	SmartPtr<PERSON> sp((SmartPtr<PERSON> *)pParam);
	g_p = sp;
	std::cout << sp->nAge << std::endl;
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	do 
	{
		HANDLE hThread[THREADCOUNT];
		SmartPtr<PERSON> p0(new PERSON{ "Jovi", 12 });
		SmartPtr<PERSON> p1(new PERSON{ "Solanin", 13 });

		SmartPtr<PERSON> p2(p1);
		const SmartPtr<PERSON> p3(p1);

		SmartPtr<PERSON> p4(p3);
		std::cout << (p3 == p1) << std::endl;
		std::cout << (p2 == p0) << std::endl;
		std::cout << (p3 != p1) << std::endl;
		std::cout << (p2 != p0) << std::endl;
		p4 = p0;
		SmartPtr<PERSON> *p = new SmartPtr<PERSON>(p1);
		for (int i = 0; i < THREADCOUNT; i++)
		{
			hThread[i] = (HANDLE)_beginthreadex(NULL, 0, testThread, (void *)p, 0, 0);
//			WaitForSingleObject(hThread[i], INFINITE);
		}
		WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE);
		delete p;
	} while (0);

	system("pause");
	return 0;
}
此处的do while(0)仅仅是我想在pause前打印出全部析构函数的输出。


对于基于引用计数器的智能指针,最致命缺点就是循环引用,导致对象被长期占用,无法释放。

以上是我对智能指针的实现和个人看法,如有不对的地方,欢迎指出。





本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5157999.html,如需转载请自行联系原作者

相关文章
|
7天前
|
存储 C++
c++的指针完整教程
本文提供了一个全面的C++指针教程,包括指针的声明与初始化、访问指针指向的值、指针运算、指针与函数的关系、动态内存分配,以及不同类型指针(如一级指针、二级指针、整型指针、字符指针、数组指针、函数指针、成员指针、void指针)的介绍,还提到了不同位数机器上指针大小的差异。
9 1
|
8天前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
16 2
|
10天前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
15天前
|
存储 C++ 索引
C++函数指针详解
【10月更文挑战第3天】本文介绍了C++中的函数指针概念、定义与应用。函数指针是一种指向函数的特殊指针,其类型取决于函数的返回值与参数类型。定义函数指针需指定返回类型和参数列表,如 `int (*funcPtr)(int, int);`。通过赋值函数名给指针,即可调用该函数,支持两种调用格式:`(*funcPtr)(参数)` 和 `funcPtr(参数)`。函数指针还可作为参数传递给其他函数,增强程序灵活性。此外,也可创建函数指针数组,存储多个函数指针。
|
23天前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
20 3
|
10天前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`-&gt;`运算符,可以方便地访问和操作智能指针所指向的对象。
|
1月前
|
C++
C++(九)this指针
`this`指针是系统在创建对象时默认生成的,用于指向当前对象,便于使用。其特性包括:指向当前对象,适用于所有成员函数但不适用于初始化列表;作为隐含参数传递,不影响对象大小;类型为`ClassName* const`,指向不可变。`this`的作用在于避免参数与成员变量重名,并支持多重串联调用。例如,在`Stu`类中,通过`this-&gt;name`和`this-&gt;age`明确区分局部变量与成员变量,同时支持链式调用如`s.growUp().growUp()`。
|
2月前
|
存储 安全 C++
C++:指针引用普通变量适用场景
指针和引用都是C++提供的强大工具,它们在不同的场景下发挥着不可或缺的作用。了解两者的特点及适用场景,可以帮助开发者编写出更加高效、可读性更强的代码。在实际开发中,合理选择使用指针或引用是提高编程技巧的关键。
27 1
|
2月前
|
安全 NoSQL Redis
C++新特性-智能指针
C++新特性-智能指针