c++ 智能指针

C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用。

为什么要使用智能指针:
智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

智能指针的原理:1.RAII特性
2.重载operator*与operator->,具有像指针一样的行为。

(1)auto_ptr的使用及问题:

//auto_ptr的实现原理:管理权转移的思想
//模拟实现一份简答的AutoPtr,了解原理
template <class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr=NULL)
		:_ptr(ptr)
	{}
	~AutoPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	//一旦发生拷贝,就将ap中的资源转移到当前对象中,然后ap与其所管理资源断开联系
	//这样就解决了一块空间被多个对象使用而造成程序崩溃问题
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}
	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		//检查是否为自己给自己赋值
		if (this != &ap)
		{
			//释放当前资源
			if (_ptr)
				delete _ptr;
			//转移zp中资源到当前对象中
			_ptr = ap._ptr;
			ap._ptr = NULL;
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};

int main()
{
	AutoPtr<Date> ap(new Date);
	//现在再从实现原理层分析会发现,这里拷贝后把ap对象的指针赋空了
	//通过ap对象访问资源时就会出现问题
	AutoPtr<Date> copy(ap);
	ap->_year = 2108;
	return 0;
}

(2)unique_ptr的实现原理:简单粗暴的防拷贝

//设计思路:防拷贝,就是不让拷贝和赋值
//模拟实现
template<class T>
class UniquePtr
{
public:
	UniquePtr(T* ptr = nullptr)
		:_ptr(ptr)
	{}
	~UniquePtr()
	{
		if (_ptr)
			delete _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T& operator->()
	{
		return _ptr;
	}
private:
	//C++98防拷贝方式:只声明+不实现+声明成私有
	UniquePtr(UniquePtr<T> const&);
	UniquePtr& operator=(UniquePtr<T> const&);

	//C++11防拷贝的方式:delete   禁止默认成员的产生
	UniquePtr(UniquePtr<T> const&) = delete;
	UniquePtr& operator=(UniquePtr<T> const&) = delete;
private:
	T* _ptr;
};

//缺陷:对象之间不能共享资源

(3)shared_ptr原理:是通过计数的方式来实现多个shared_ptr对象之间共享资源。

<1>shared_ptr在其内部,给每个资源都维护了一份计数,用来记录该份资源被几个对象共享
<2>在对象被销毁时,就说明自己不使用该资源了,对象的引用计数减1
<3>如果引用计数时0,就说明自己是最后一个使用该资源的对象,必须释放资源
<4>如果不是0,就说明除了自己还有其他对象正在使用该份资源,不能释放资源,否则其他对象就成为野指针了。

//模拟实现一份简单的shard_ptr,了解原理
#include <thread>
#include <mutex>

template <class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		:_ptr(ptr)
		,_pRefCount(new int(1))
		,_pMutex(new mutex)
	{}
	~SharedPtr()
	{
		Release();
	}
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)
		, _pRefCount(sp._pRefCount)
		, _pMutex(sp._pMutex)
	{
		AddRefCount();
	}
	//sp1=sp2
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		//if(this!=&sp)
		if (_ptr != sp._ptr)
		{
			//释放管理的旧资源
			Release();

			//共享管理新对象的资源,并增加引用计数
			_ptr = sp._pMutex;
			_pRefCount = sp._pRefCount;
			_pMutex = sp._pMutex;

			AddRefCount();
		}
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	int UseCount()
	{
		*_pRefCount;
	}
	T* Get()
	{
		return _ptr;
	}
     
	void AddRefCount()
	{
		//加锁或者使用加1的原子操作
		_pMutex->lock();
		++(*_pRefCount);
		_pMutex->unlock();
	}

private:
	void Release()
	{
		bool deleteflag = false;
		//引用计数减1,如果减至0,则释放资源
		_pMutex.lock();
		if (--(*_pRefCount) == 0)
		{
			delete _ptr;
			delete _pRefCount;
			deleteflag = true;
		}
		_pMutex.unlock();
		if (deleteflag == true)
			delete _pMutex;
	}

private:
	//关于引用计数的类型:
	//   如果为普通类型比如int count  sp2(sp1)当对sp2中的count--时,sp1中的count不会发生变化
	//   如果设置为static int count    shared_ptr <int> sp3 count为所有类对象所共享,
	//           那么count会变为1,sp1,sp2中的count也会改变为1,那么就会受影响  
	//计数应该与资源挂钩,所以采用指针的方式,直接指向资源的空间
	int* _pRefCount;   //引用计数
	T* _ptr;    //指向管理资源的指针
	mutex* _pMutex;    //互斥锁
};

shared_ptr用来处理delete malloc FILE 至于delete[]可以直接用vector处理
资源的类型:new的堆空间 delete的堆空间 文件指针 套接字

shared_ptr:定制删除 让shared_pt释放资源的方式:采用仿函数
具体资源,让用户根据资源的类型选择合适的方式进行释放

shared_ptr的线程安全问题:双向链表
C++11引入weak_ptr作用:专门解决shared_ptr中存在的循环引用

#include <memory>
#include <iostream>
using namespace std;

struct ListNode
{
	ListNode(int data)
		/*:next(nullptr)
		,prev(nullptr)
		,_data(data)*/
		:_data(data)
	{
		cout << "ListNode(int):" << this << endl;
	}
	~ListNode()
	{
		cout << "~ListNode():" << this << endl;
	}
	//存在循环引用
	/*shared_ptr<ListNode> next;
	shared_ptr<ListNode> prev;*/
	weak_ptr<ListNode> next;
	weak_ptr<ListNode> prev;
	int _data;
};

void TestSharedPtr()
{
	shared_ptr<ListNode> sp1(new ListNode(10));
	shared_ptr<ListNode> sp2(new ListNode(20));

	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;

	sp1->next = sp2;
	sp2->prev = sp1;

	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}

weak_ptr不能单独管理资源—>配合shared_ptr一起使用
weak_ptr没有提供可以接受资源的构造函数
weak_ptr,在连接期间,没有增加引用计数—>use_count

google-perftools 简介 google-perftools 是一款针对 C/C++ 程序的性能分析工具,它是一个遵守 BSD 协议的开源项目。使用该工具可以对 CPU 时间片、内存等系统资源的分配和使用进行分析,本文将重点介绍如何进行 CPU 时间片的剖析。 google-perftools 对一个程序的 CPU 性能剖析包括以下几个步骤。 1. 编译目标程序,加入对 google-perftools 库的依赖。 2. 运行目标程序,并用某种方式启动 / 终止剖析函数并产生剖析结果。 3. 运行剖结果转换工具,将不可读的结果数据转化成某种格式的文档(例如 pdf,txt,gv 等)。 安装 您可以在 google-perftools 的网站 (http://code.google.com/p/google-perftools/downloads/list) 上下载最新版的安装包。为完成步骤 3 的工作,您还需要一个将剖析结果转化为程序员可读文档的工具,例如 gv(http://www.gnu.org/software/gv/)。 编译与运行 您需要在原有的编译选项中加入对 libprofiler.so 的引用,这样在目标程序运行时会加载工具的动态库。例如本例中作者的系统中,libprofiler.so 安装在"/usr/lib"目录下,所以需要在 makefile 文件中的编译选项加入“-L/usr/lib -lprofiler”。 google-perftools 需要在目标代码的开始和结尾点分别调用剖析模块的启动和终止函数,这样在目标程序运行时就可以对这段时间内程序实际占用的 CPU 时间片进行统计和分析。工具的启动和终止可以采用以下两种方式。 a. 使用调试工具 gdb 在程序中手动运行性能工具的启动 / 终止函数。 gdb 是 Linux 上广泛使用的调试工具,它提供了强大的命令行功能,使我们可以在程序运行时插入断点并在断点处执行其他函数。具体的文档请参照 http://www.gnu.org/software/gdb/,本文中将只对用到的几个基本功能进行简单介绍。使用以下几个功能就可以满足我们性能调试的基本需求,具体使用请参见下文示例。 命令 功能 ctrl+c 暂停程序的运行 c 继续程序的运行 b 添加函数断点(参数可以是源代码中的行号或者一个函数名) p 打印某个量的值或者执行一个函数调用 b. 在目标代码中直接加入性能工具函数的调用,该方法就是在程序代码中直接加入调试函数的调用。 两种方式都需要对目标程序重新编译,加入对性能工具的库依赖。对于前者,他的好处是使用比较灵活,但工具的启动和终止依赖于程序员的手动操作,常常需要一些暂停函数(比如休眠 sleep)的支持才能达到控制程序的目的,因此精度可能受到影响。对于后者,它需要对目标代码的进行修改,需要处理函数声明等问题,但得到的结果精度较高,缺点是每次重新设置启动点都需要重新编译,灵活度不高,读者可以根据自己的实际需求采用有效的方式。 示例详解 该程序是一个简单的例子,文中有两处耗时的无用操作,并且二者间有一定的调用关系。 清单 1. 示例程序 void consumeSomeCPUTime1(int input){ int i = 0; input++; while(i++ < 10000){ i--; i++; i--; i++; } }; void consumeSomeCPUTime2(int input){ input++; consumeSomeCPUTime1(input); int i = 0; while(i++ < 10000){ i--; i++; i--; i++; } }; int stupidComputing(int a, int b){ int i = 0; while( i++ < 10000){ consumeSomeCPUTime1(i); } int j = 0; while(j++ < 5000){ consumeSomeCPUTime2(j); } return a+b; }; int smartComputing(int a, int b){ return a+b; }; void main(){ int i = 0; printf("reached the start point of performance bottle neck\n"); sleep(5); //ProfilerStart("CPUProfile"); while( i++ MyProfile.pdf 转换后产生的结果文档如下图。图中的数字和框体的大小代表了的某个函数的运行时间占整个剖析时间的比例。由代码的逻辑可知,stupidComputing,stupidComputing2 都是费时操作并且它们和 consumeSomeCPUTime 存在着一定的调用关系。 图 1. 剖析结果 结束语 本文介绍了一个 Linux 平台上的性能剖析工具 google-perftools,并结合实例向读者展示了如何使用该工具配置、使用及分析性能瓶颈。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值