【第21期】观点:人工智能到底用 GPU?还是用 FPGA?

RAII&智能指针

原创 2016年05月30日 13:35:22

     智能指针是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;
};

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

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


其实,我们首先介绍的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仍然存在许多的缺陷,它可能引入:线程安全问题,循环引用问题,删除器不匹配

*线程安全百度百科

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

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库辣~\(≧▽≦)/~i_f26.gif

本文出自 “Zimomo” 博客,请务必保留此出处http://zimomo.blog.51cto.com/10799874/1760151

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

RAII&智能指针

RAII:资源分配及初始化。但是这个翻译并没有显示出这个惯用法的真正内涵。RAII的好处在于它提供了一种资源自动管理的方式,当出现异常,回滚等现象时,RAII可以正确的释放资源。内存泄漏会导致:   ...

Android 智能指针原理

Android系统的运行时库层代码是用C++来编写的,用C++来写代码最容易出错的地方就是指针了,一旦使用不当,轻则造成内存泄漏,重则造成系统崩溃。不过系统为我们提供了智能指针,避免出现上述问题,本文将系统地分析Android系统智能指针(轻量级指针、强指针和弱指针)的实现原理。 <p align="left" style="color: rgb(51, 51, 51);

RAII和智能指针的实现

RAII在C++effective一书中讲到,RAII是“Resource acquisition is initialization”,直译为“资源获取就是初始化”。它是基于这样的原理,栈的变量会自...

感悟3:智能指针 = 引用计数 + RAII

auto_ptr的一个重要思想是RAII.RAII是"Resource Acquisition Is Initialisation"的简称,有人把它翻译成"资源获取即初始化",但我更喜欢用理解的方式把RAII看成是利用对象的生命周期管理资源。RAII思想不只是局限于C++语言,但毫无疑问它跟C++是绝配。 RAII这一思想,特别是运用在C++语言上,不仅实用、简单,而且高效。把资源的管理交给了特定的资源管理对象,这就把资源的管理局部化在资源管理对象上。资源管理对象主要利用了对象的生命周期达到了自我管理,使得实现极其简单。效率方面,在C++中资源管理对象的所有操作几乎都可以inli

C++编程规范之13:确保资源为对象所拥有。使用显示的RAII和智能指针

摘要:     利器在手,不要再徒手为之:C++的“资源获取及初始化”(ResourceAcquisition Is Initialization,RAII)惯用法是正确处理资源的利器。RAII使编...

boost 智能指针 --- 关于性能的少数派报告

&lt;iframe align="center" marginwidth="0" marginheight="0" src="http://www.zealware.com/csdnblog.html" frameborder="0" width="728" scrolling="no" height="90"&gt;&lt;/iframe&gt; 开门见山好了,boost 1.33 对于 boost 1.32 的 shared_ptr 和 weak_ptr 有一个不小的改变,然而这个改变如此透明,以至于它甚至于没有出现在 boost 1.33

boost之路 二 智能指针

 智能指针的原理基于一个常见的习语叫做 RAII :资源申请即初始化。 智能指针只是这个习语的其中一例——当然是相当重要的一例。 智能指针确保在任何情况下,动态分配的内存都能得到正确释放,从而将...

Android源码之智能指针

No.1 智能指针的设计 智能指针是通过引用技术来控制对象的生成和销毁的。我们知道在才C++中对象的生成和销毁会自动调用对象的构造函数和析构函数,所以智能指针的大部分逻辑都集中在构造函数和析构函数中,这也是我们分析的重点。 因为Android的职能指针是通过引用计数的方法来实现的。所以在设计中会涉及到两个层次。第一层是定义控制引用计数的对象,我们称之为引用计数控制类。第二层才是对真实对象的访问,可以把这个对象看作是对真实对象的包裹。所以我们把这个类称之为包裹类。 这样职能指针会自动帮你控制对象的创建和销毁了。 在Android系统里,主要存在三种职能指针

C++智能指针深层剖析,模拟实现

C++中使用new和delete动态开辟和释放内存。但有时我们会忘记释放内存,这样就有可能出现内存泄漏等一些危险的情况。有时在尚有指针引用内存的情况下我们就释放了它,在这种情况下就会产生引用非法内存的...

ATL智能指针类剖析

CComPtr CComPtr派生自模板类CComPtrBase&lt;T&gt;,T代表某个COM接口。CComPtrBase&lt;T&gt;类只有一个成员变量T* p。下面是它们的代码分析: 构造函数 protected: CComPtrBase() throw() { p = NULL; } CComPtrBase(_In_ int nNu
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)