RAII&智能指针

智能指针是C++中为了实现资源的有效管理而被提出的,我们可以创建它但无须操心它的释放问题,在引入异常机制的程序里它是十分有用的,或者说,对于博主这中粗心大意的人来说还是可以偶尔使用的。他可以在一些场合防止内存泄漏的问题。但是,智能指针也是存在着许多的问题,所以许多的编程规范里告诫我们少使用智能指针,但对于我们来说,必须了解它的原理。



*RAII:资源获得即初始化,我们在构造函数里将其初始化,并在析构函数里释放它

eg:一个简单的AutoPtr的实现

template<class T>
class AutoPtr
{
	friend ostream& operator <<(ostream&os, const AutoPtr& ap)
	{
		os << ap._ptr;
		return os;
	}
public:
	AutoPtr(T * p) :_ptr(p)
	{}
	AutoPtr(AutoPtr & ap)
	{
		_ptr = ap._ptr;
		ap._ptr = NULL;
	}
	AutoPtr& operator =(AutoPtr & ap)
	{
		if (ap._ptr != _ptr)
		{
			delete _ptr;
			_ptr = ap._ptr;
			ap._ptr = NULL;
		}
		return *this;
	}
	T& operator *()
	{
		return *_ptr;
	}
	T* operator ->()
	{
		return _ptr;
	}
	~AutoPtr()
	{
		if (_ptr != NULL)
		{
			delete _ptr;
		}
	}
private:
	T* _ptr;
};

我们可以看出,上面的程序问题在哪里呢,我们力求来模仿原生指针让多个AutoPtr管理同一块空间但是更加严重的问题出现:我们将管理权限仅交给最后一个AutoPtr,但是如果有的客户程序员们不了解这个机制而将其当做普通的指针来使用,如果他使用了没有管理权限的指针将发生错误!

为了避免这样的错误出现,我们使用了一种简单暴力的方拷贝机制处理从而使得ScopedPtr粗线啦

template<class T>
class ScopedPtr
{
public:
	ScopedPtr(T* ptr) :_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr != NULL)
			delete _ptr;
	}
	T& operator *()
	{
		return *_ptr;
	}
	T* operator ->()
	{
		return _ptr;
	}
private:
	T* _ptr;
	ScopedPtr(ScopedPtr<T>& sp);
	ScopedPtr& operator =(ScopedPtr<T>& sp);
};

为了避免这种错误发现,我们可以直接拒绝客户程序员做拷贝构造和赋值操作,使得我们的程序变得更加安全,遗憾的是,这样我们的指针就不能做到和原生指针一样可以使用多个指针共同维护一块空间了。

*防拷贝的实现:把拷贝构造及赋值运算符的重载声明成私有/保护成员,并且只声明不定义。

然而这样的智能指针似乎还是不太理想。所以我们又发现,可以使用引用计数的方法使得我们的指针能更加像原生指针的行为,于是,我们的SharedPtr出现

template <class T>
class SharedPtr
{
public:
	SharedPtr(T * p) :_ptr(p), _pCount(new int(1))
	{
	}
	SharedPtr(SharedPtr& sp)
	{
		_ptr = sp._ptr;
		(*sp._pCount)++;
		_pCount = sp._pCount;
	}
	SharedPtr& operator=(SharedPtr sp)
	{
		swap(_ptr,sp._ptr);
		swap(_pCount, sp._pCount);
		return *this;
	}
	T* operator ->()
	{
		return _ptr;
	}
	T& operator *()
	{
		return *_ptr;
	}
	~SharedPtr()
	{
		if (--(*_pCount) == 0)
		{
			delete _ptr;
			delete _pCount;
		}
	}
	int GetCount()
	{
		return *_pCount;
	}
private:
	T * _ptr;
	int *_pCount;
};

(以上代码为现代写法,如果这个时候你还是挺闲的就别忘记把传统写法也实现下啵

是不是觉得有点赞,不得不说,想出这些方法的大大们还是棒棒的。

其实,我们首先介绍的AutoPtr的实现方法还有另一种,不过现在大家已经基本抛弃了这种写法。

eg:AutoPtr实现方法二

#include<iostream>
//*************AutoPtrWithOwner***********
template<class T>
class AutoPtrWithOwner
{
public:
	AutoPtrWithOwner(T* ptr) :_ptr(ptr), _owner(true)
	{}
	ScopedPtrWithOwner(AutoPtrWithOwner& sp)
	{
		_ptr = sp._ptr;
		_owner = true;
		sp._owner = false;
	}
	AutoPtrWithOwner& operator=(AutoPtrWithOwner& sp)
	{
		if (!sp._owner)
		{
			std::cout << "实参没有管理权限!请勿非法操作!" << std::endl;
		}
		else if (_ptr != sp._ptr)
		{
			if (_owner == true)
				delete _ptr;
			_ptr = sp._ptr;
			_owner = true;
			sp._owner = false;
		}
		return *this;
	}
	~AutoPtrWithOwner()
	{
		if (_owner)
			delete _ptr;
	}
private:
	T *_ptr;
	bool _owner;
};

相比于我们介绍的第一种方式,似乎这种方式的安全性并不高,它和产生垂悬指针哦,就是我们所说的野指针。所以请你尽量不要使用这种方式。

我们看到上面的例子中已经介绍了三种智能指针

那么博主都认识哪些智能指针呢?

1.auto ptr 实现:管理权限的转移

2.shared ptr 实现:引用计数

3.scoped ptr 实现:防止拷贝

4.weak ptr (弱指针,用于辅助shared ptr)

实际上shared ptr仍然存在许多的缺陷,它可能引入:线程安全问题,循环引用问题,删除器不匹配

*线程安全:百度百科

今天我们主要为大家解决后两个问题(其实是因为博主还没有学习多线程编程,不会啦,夭寿啊!

1.定制删除器(使用模板实现)

#include<iostream>
#define _CRT_SECURE_NO_WARNINGS 1
//**************定制删除器的实现***********
template<class T,class Del=Delete<T> >
class SharedPtr
{
public:
	/*SharedPtr(T *ptr, Del del) :_ptr(ptr), _del(del), _pCount(new int(1))
	{}*/
	SharedPtr(T *ptr) :_ptr(ptr), _pCount(new int(1))
	{}
	SharedPtr(const SharedPtr& sp)
	{
		_ptr = sp._ptr;
		_pCount = sp._pCount;
		(*_pCount)++;
	}
	SharedPtr& operator =(SharedPtr  sp)
	{
		swap(_ptr, sp._ptr);
		swap(_ptr, sp._pCount);
		return *this;
	}
	~SharedPtr()
	{
		_Relese();
	}
private:
	T *_ptr;
	T *_pCount;
	Del _del;
	void  _Relese()
	{
		if (--(*_pCount) == 0)
		{
			_del(_ptr);
			_del(_pCount);
		}
	}
};
template <class T>
struct Free
{
	void operator() (void *sp)
	{
		free(sp);
		sp = NULL;
	}
};
template <class T>
struct Delete
{
	void operator() (const T*sp)
	{
		delete sp;
	}
};
template <class T>
struct Fclose
{
	void operator() (void *sp)
	{
		fclose(sp);
		sp = NULL;
	}
};
void testSharePtrDelete()
{
	SharedPtr<int> sp1(new int(5));
	SharedPtr<int> sp2(sp1);
}
void testSharePtrFree()
{
	SharedPtr<int,Free<int>> sp1((int *)malloc(sizeof(int)*10));
	SharedPtr<int,Free<int>> sp2(sp1);
}
void testSharePtrFclose()
{
	FILE *pf = fopen("","r");
	SharedPtr<FILE, Fclose<FILE>> sp1(pf);
	SharedPtr<FILE, Fclose<FILE>> sp2(sp1);
}

*仿函数:使用operator()使得对象方法的调用形式像一般的函数一样

2.循环引用

什么是循环引用嘞?我们来举栗子好了

Struct Node
{
    shared_ptr<Node> _next;
    shared_ptr<Node> _prev;
    int _data;
    Node(int a):_data(a),_next(NULL),_prev(NULL)
    {}
    ~Node()
    {
        cout<<"~Node()"<<endl;
    }
};
void Test()
{
    shared_ptr<Node> cur(new Node(1));
    shared_ptr<Node> next(new Node(2));
    cur -> _next = next;
    next -> _prev = cur;
}

上面的程序就出现了循环引用的问题我们可以看见我们调用之后并没有调用析构函数,这样就引起了内存泄漏的问题,为什么呢,因为其每一个节点一开始被一个shared_ptr指着,后来你在实例化之后因为他又被他前面或者后面的节点所指,引用计数发生了增加,所以在析构的时候检查到自己的引用计数没有减到0所以没有释放本来该释放的空间。

其实这个问题是很好解决的,我们将Node里面的shared_ptr换成weak_ptr就可以完美解决,在此处,weak_ptr辅助了shared_ptr而没有增加引用计数~

博主要去了解尾大的民间组织写出的boost库辣~\(≧▽≦)/~

RAII&智能指针

时间: 2024-10-18 06:17:29

RAII&智能指针的相关文章

浅谈RAII&智能指针

关于RAII,官方给出的解释是这样的"资源获取就是初始化".听起来貌似不是很懂的哈,其实说的通俗点的话就是它是一种管理资源,避免内存泄漏的一种方法.它可以保证在各种情况下,当你对对象进行使用时先通过构造函数来进行资源的分配和初始化,最后通过析构函数来进行清理,有效的保证了资源的正确分配和释放.(特别是在异常中,因为异常往往会改变代码正确的执行顺序,这就很容易引起资源管理的混乱和内存的泄漏) 其中智能指针就是RAII的一种实现模式,所谓的智能就是它可以自动化的来管理它所指向那份空间的资源

C++ 资源管理(RAII)--智能指针

1. 智能指针(Smart Pointer) i. 是存储指向动态分配(堆)对象指针的类 ii. 在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象 iii. RAII类模拟智能指针,见备注 2. C++11提供了以下几种智能指针,位于头文件<memory>,它们都是模板类 i. std::auto_ptr(复制/赋值) ii. std::unique_ptr  c++11 iii.std::shared_ptr  c++11 iv.std::weak_ptr    c++11

智能指针

RAII(Resource Acquisition Is Initialization) 资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放. ------------------------------------------------------------------------------------------- 为什么要有智能指针呢? 智能指针的存在是为了防止我们在大型项目中忘记释放空间而导

使用智能指针来管理对象 (基于RAII)

////一个简单的防止内存泄露的例子//void test() { //使用RAII的特性管理资源 //当智能指针unique_ptr被销毁时,它指向的对象也将被销毁 //这里test函数返回后 p将自动销毁 //unique_ptr<int[]> p( new int[200] ); //直接生成资源 //test函数返回后 p不能被正常销毁,就会造成资源泄露 //int* p = new int[200]; } int main() { while( 1 ) { test(); Sleep

C++的RAII和智能指针小结

RAII:资源分配即初始化,利用构造函数和析构函数定义一个类来完成对资源的分配和释放 智能指针主要用来防止内存泄漏,我们来举个栗子,看看为什么会有智能指针这个东东 例1: 对于上面这段程序,由于抛出异常的时候影响了代码的执行流,所以要在异常捕获之前将p提前释放(详见 我的博客:C++的异常浅析),虽然可以通过再次抛出以异常解决这个问题,但是在代码美观方面不够完 美,更重要的是如果在代码量非常大,而且在多处有动态开辟出来的空间的时候,仅仅通过再次抛出异常已 经远远不够解决这个问题了,会使得工作量大

RAII和模拟实现智能指针

什么叫RAII(Resource Acquisition Is Initialization)? RAII指的就是资源分配即初始化,它的作用就是定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放.它是一种技术. 为什么要使用RAII? 在堆上分配空间时,我们必须很仔细的申请并给出相应的释放语句,但是随着程序的复杂度增大,判断.循环.递归这样的语句会让程序走向不确定,很有可能出现申请了没释放,申请了多次释放.所以我们定义了一

RAII思想之智能指针

RAII(Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种利用对象生命周期来控制资源的技术. 简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源. 这种技术是C++为了控制资源,避免内存泄漏的惯用法. // 使用RAII思想设计的SmartPtr类 template<class T> class SmartPtr { public

智能指针tr1::shared_ptr、boost::shared_ptr使用

对于tr1::shared_ptr在安装vs同时会自带安装,但是版本较低的不存在.而boost作为tr1的实现品,包含 "Algorithms Broken Compiler Workarounds Concurrent Programming Containers Correctness and Testing Data Structures Domain Specific Function Objects and Higher-order Programming Generic Progra

【C++】智能指针详解(三):scoped_ptr

在介绍scoped_ptr之前,我们先回顾一下前两篇文章的内容. 首先,智能指针采用RAII机制,通过对象来管理指针,构造对象时,完成资源的初始化;析构对象时,对资源进行清理及汕尾. auto_ptr,通过转移管理权来完成对象的拷贝与赋值,在实际开发中并不实用. 回顾完智能指针的背景及auto_ptr的特性之后,本文来介绍scoped_ptr的实现原理及特性. scoped_ptr与auto_ptr类似,但最大的区别就是它不能转让管理权.也就是说,scoped_ptr禁止用户进行拷贝与赋值. 诶