C/C++使用Lu键树实现智能指针及检查内存泄露

欢迎访问Lu程序设计

C/C++使用Lu键树实现智能指针及检查内存泄露

1 说明

    要演示本文的例子,你必须下载Lu32脚本系统。本文的例子需要lu32.dll、lu32.lib、C++格式的头文件lu32.h,相信你会找到并正确使用这几个文件。

    用C/C++编译器创建一个控制台应用程序,复制本文的例子代码直接编译运行即可。

2 关于智能指针及Lu实现

    由于 C/C++ 语言没有自动内存回收机制,程序员每次 malloc/new 出来的内存都要手动 free/delete。由于程序流程太复杂,程序员容易忘记 free/delete,造成内存泄露。C++用智能指针可以有效缓解这类问题,例如:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost::intrusive_ptr等等,可谓种类繁多。

    实际上,C/C++程序使用Lu脚本的键树系统也可以解决此类问题,而且程序员可以方便地查找哪块内存忘记了free/delete。阅读完本文,你就可以尝试使用Lu脚本系统查找你的C/C++程序是否存在内存泄露了。

    关于Lu脚本键树系统,可以参考Lu编程指南,同时参考这个教程:C/C++使用Lu脚本字符串键树

3 代码

代码1:实现智能指针

#include <windows.h>
#include <iostream>
#include "lu32.h"

#pragma comment( lib, "lu32.lib" )

using namespace std;

#define key_Simple		(luPriKey_User-20)	//将对该私有键加锁
#define imax		5		//私有键值最大数

void _stdcall Del_Simple(void *me);		//销毁Simple的函数

class Simple		//编写一个简单的类帮助测试
{
public:
	int number;	//数据
	bool bDelete;	//为帮助测试新增的数据,对象初始化时bDelete=false,对象销毁时bDelete=true

	Simple(int param=0)
	{
		//在类的初始化函数中增加以下几行代码
		void *me,*NowKey=NULL;	//为避免编译器发出警告进行初始化,实际上不需要
		me=this;
		InsertKey((char *)&(me),sizeof(luVOID),key_Simple,this,Del_Simple,NULL,1,NowKey);	//在Lu键树中注册键值
		bDelete=false;

		number = param;
		cout << "Simple: " << number << endl;
	};

	~Simple()
	{
		//在类的初始化函数中增加以下几行代码
		void *me;
		if(!bDelete)	//如果用户手动delete
		{
			bDelete=true;
			me=this;
			DeleteKey((char *)&(me),sizeof(luVOID),key_Simple,Del_Simple,0);	//在Lu键树中删除键值
		}

		cout << "~Simple: " << number << endl;
	};
};
void _stdcall Del_Simple(void *me)	//销毁Simple的函数,将注册到Lu系统,以实现自动销毁Simple
{
	Simple *pSimple;

	pSimple=(Simple*)me;
	if(pSimple->bDelete==false)		//如果用户忘记了delete
	{
		pSimple->bDelete=true;
		delete pSimple;		//这是Lu系统自动delete
	}
}
LuData _stdcall OpLock_Simple(luINT m,LuData *Para,void *hFor,int theOperator)	//Simple的运算符重载函数
{
	LuData a;

	a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;	//不对Simple类型的对象做运算,总返回nil
	return a;
}

void main(void)
{
	Simple *pSimple[imax];
	int i;

	if(!InitLu()) return;	//初始化Lu

	LockKey(key_Simple,Del_Simple,OpLock_Simple);	//在Lu键树中加锁键,只能存储Simple类型

	for(i=0;i<imax;i++)
	{
		pSimple[i]=new Simple(i);
	}

	cout << endl << "--- C++ delete 删除约1/2对象 ---" << endl << endl;
	for(i=0;i<imax/2;i++)
	{
		delete pSimple[i];
	}

	cout << endl << "--- Lu系统解锁键并销毁全部对象 ---" << endl << endl;
	LockKey(key_Simple,NULL,OpLock_Simple);	//在Lu键树中解锁键

	FreeLu();		//释放Lu
}


运行结果:

Simple: 0
Simple: 1
Simple: 2
Simple: 3
Simple: 4

--- C++ delete 删除约1/2对象 ---

~Simple: 0
~Simple: 1

--- Lu系统解锁键并销毁全部对象 ---

~Simple: 2
~Simple: 4
~Simple: 3

代码2:查找未释放对象(内存)

#include <windows.h>
#include <iostream>
#include "lu32.h"

#pragma comment( lib, "lu32.lib" )

using namespace std;

#define key_Simple		(luPriKey_User-20)	//将对该私有键加锁
#define imax		5		//私有键值最大数

void _stdcall Del_Simple(void *me);		//销毁Simple的函数

class Simple		//编写一个简单的类帮助测试
{
public:
	int number;	//数据
	bool bDelete;	//为帮助测试新增的数据,对象初始化时bDelete=false,对象销毁时bDelete=true

	Simple(int param=0)
	{
		//在类的初始化函数中增加以下几行代码
		void *me,*NowKey=NULL;	//为避免编译器发出警告进行初始化,实际上不需要
		me=this;
		InsertKey((char *)&(me),sizeof(luVOID),key_Simple,this,Del_Simple,NULL,1,NowKey);	//在Lu键树中注册键值
		bDelete=false;

		number = param;
		cout << "Simple: " << number << endl;
	};

	~Simple()
	{
		//在类的初始化函数中增加以下几行代码
		void *me;
		if(!bDelete)	//如果用户手动delete
		{
			bDelete=true;
			me=this;
			DeleteKey((char *)&(me),sizeof(luVOID),key_Simple,Del_Simple,0);	//在Lu键树中删除键值
		}

		cout << "~Simple: " << number << endl;
	};
};
void _stdcall Del_Simple(void *me)	//销毁Simple的函数,将注册到Lu系统,以实现自动销毁Simple
{
	Simple *pSimple;

	pSimple=(Simple*)me;
	if(pSimple->bDelete==false)		//如果用户忘记了delete
	{
		pSimple->bDelete=true;
		delete pSimple;		//这是Lu系统自动delete
	}
}
LuData _stdcall OpLock_Simple(luINT m,LuData *Para,void *hFor,int theOperator)	//Simple的运算符重载函数
{
	LuData a;

	a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;	//不对Simple类型的对象做运算,总返回nil
	return a;
}
int _stdcall GetKeySimpleValue(char *KeyStr,int ByteNum,void *KeyValue)	//枚举Lu系统中指定键值类型的所有对象
{
	Simple *pSimple;

	pSimple=(Simple *)KeyValue;
	cout << "Simple->number: " << pSimple->number << endl;
	return 1;
}

void main(void)
{
	Simple *pSimple[imax];
	int i;
	char KeyStr[sizeof(luVOID)+1];	//枚举未销毁的对象用,长度比指针长度大1

	if(!InitLu()) return;	//初始化Lu

	LockKey(key_Simple,Del_Simple,OpLock_Simple);	//在Lu键树中加锁键,只能存储Simple类型

	for(i=0;i<imax;i++)
	{
		pSimple[i]=new Simple(i);
	}

	cout << endl << "--- C++ delete 删除约1/2对象 ---" << endl << endl;
	for(i=0;i<imax/2;i++)
	{
		delete pSimple[i];
	}

	cout << endl << "--- 使用Lu枚举未释放的所有对象 ---" << endl << endl;
	EnumKey(key_Simple,KeyStr,sizeof(luVOID)+1,GetKeySimpleValue);

	cout << endl << "--- Lu系统解锁键并销毁全部对象 ---" << endl << endl;
	LockKey(key_Simple,NULL,OpLock_Simple);		//在Lu键树中解锁键

	FreeLu();			//释放Lu
}


运行结果:

Simple: 0
Simple: 1
Simple: 2
Simple: 3
Simple: 4

--- C++ delete 删除约1/2对象 ---

~Simple: 0
~Simple: 1

--- 使用Lu枚举未释放的所有对象 ---

Simple->number: 2
Simple->number: 4
Simple->number: 3

--- Lu系统解锁键并销毁全部对象 ---

~Simple: 2
~Simple: 4
~Simple: 3

代码3:效率测试之一,未使用Lu缓冲区

#include <windows.h>
#include <iostream>
#include <time.h>
#include "lu32.h"

#pragma comment( lib, "lu32.lib" )

using namespace std;

#define key_Simple		(luPriKey_User-20)	//将对该私有键加锁

#define imax		10	//私有键值最大数,修改此数据时要同时修改下面的循环次数kmax
int kmax = 100000;			//循环次数

void _stdcall Del_Simple(void *me);		//销毁Simple的函数

class Simple		//编写一个简单的类帮助测试:使用Lu系统
{
public:
	int number;	//数据
	bool bDelete;	//为帮助测试新增的数据,对象初始化时bDelete=false,对象销毁时bDelete=true

	Simple(int param=0)
	{
		//在类的初始化函数中增加以下几行代码
		void *me,*NowKey=NULL;	//为避免编译器发出警告进行初始化,实际上不需要
		me=this;
		InsertKey((char *)&(me),sizeof(luVOID),key_Simple,this,Del_Simple,NULL,1,NowKey);	//在Lu键树中注册键值
		bDelete=false;

		//初始化代码
		number = param;
	};

	~Simple()
	{
		//在类的初始化函数中增加以下几行代码
		void *me;
		if(!bDelete)	//如果用户手动delete
		{
			bDelete=true;
			me=this;
			DeleteKey((char *)&(me),sizeof(luVOID),key_Simple,Del_Simple,0);	//在Lu键树中删除键值
		}

		//销毁对象的其他代码
	};
};
void _stdcall Del_Simple(void *me)	//销毁Simple的函数,将注册到Lu系统,以实现自动销毁Simple
{
	Simple *pSimple;

	pSimple=(Simple*)me;
	if(pSimple->bDelete==false)		//如果用户忘记了delete
	{
		pSimple->bDelete=true;
		delete pSimple;		//这是Lu系统自动delete
	}
}
LuData _stdcall OpLock_Simple(luINT m,LuData *Para,void *hFor,int theOperator)	//Simple的运算符重载函数
{
	LuData a;

	a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;	//不对Simple类型的对象做运算,总返回nil
	return a;
}

class SimpleCpp		//编写一个简单的类帮助测试:不使用Lu系统
{
public:
	int number;	//数据

	SimpleCpp(int param=0)
	{
		//初始化代码
		number = param;
	};

	~SimpleCpp()
	{
		//销毁对象的其他代码
	};
};

void main(void)
{
	Simple *pSimple[imax];
	SimpleCpp *pSimpleCpp[imax];
	int i,j;
	clock_t start, end;

	start = clock();
	for(j=0;j<kmax;j++)
	{
		for(i=0;i<imax;i++)
		{
			pSimpleCpp[i]=new SimpleCpp(i);
		}
		for(i=0;i<imax;i++)
		{
			delete pSimpleCpp[i];
		}
	}
	end = clock();
	cout << "--- C/C++ 系统运行时间 : " << end - start << " ms." << endl;

	////////////////////////////////////////////////////////////////////////////

	if(!InitLu()) return;	//初始化Lu

	LockKey(key_Simple,Del_Simple,OpLock_Simple);	//在Lu键树中加锁键,只能存储Simple类型

	start = clock();
	for(j=0;j<kmax;j++)
	{
		for(i=0;i<imax;i++)
		{
			pSimple[i]=new Simple(i);
		}
		for(i=0;i<imax;i++)
		{
			delete pSimple[i];
		}
	}
	end = clock();
	cout << "--- C/C++ Lu 复合系统运行时间 : " << end - start << " ms." << endl;

	LockKey(key_Simple,NULL,OpLock_Simple);	//在Lu键树中解锁键

	FreeLu();		//释放Lu
}


运行结果:

--- C/C++ 系统运行时间 : 218 ms.
--- C/C++ Lu 复合系统运行时间 : 10578 ms.

代码4:效率测试之二,使用Lu缓冲区

#include <windows.h>
#include <iostream>
#include <time.h>
#include "lu32.h"

#pragma comment( lib, "lu32.lib" )

using namespace std;

#define key_Simple		(luPriKey_User-20)	//将对该私有键加锁

#define imax		10	//私有键值最大数,修改此数据时要同时修改下面的循环次数kmax
int kmax = 100000;			//循环次数

void _stdcall Del_Simple(void *me);		//销毁Simple的函数

class Simple		//编写一个简单的类帮助测试:使用Lu系统
{
public:
	int number;	//数据
	bool bDelete;	//为帮助测试新增的数据,对象初始化时bDelete=false,对象销毁时bDelete=true

	Simple(int param=0)
	{
		//在类的初始化函数中增加以下几行代码
		void *me,*NowKey=NULL;	//为避免编译器发出警告进行初始化,实际上不需要
		me=this;
		InsertKey((char *)&(me),sizeof(luVOID),key_Simple,this,Del_Simple,NULL,1,NowKey);	//在Lu键树中注册键值
		bDelete=false;

		//初始化代码
		number = param;
	};

	~Simple()
	{
		//在类的初始化函数中增加以下几行代码
		void *me;
		if(!bDelete)	//如果用户手动delete
		{
			bDelete=true;
			me=this;
			DeleteKey((char *)&(me),sizeof(luVOID),key_Simple,Del_Simple,0);	//在Lu键树中删除键值
		}

		//销毁对象的其他代码
	};
};
Simple *NewSimple(int param=0)	//设计专用的生成Simple的函数
{
	Simple *pSimple;
	char keyname[sizeof(luVOID)];

	pSimple=(Simple *)GetBufObj(key_Simple,keyname);	//先尝试从缓冲区中获取一个Simple对象
	if(pSimple)		//获得对象后初始化
	{
		pSimple->number=param;
	}
	else			//生成新的对象并初始化
	{
		pSimple=new Simple(param);
	}
	return pSimple;
}
void _stdcall Del_Simple(void *me)	//销毁Simple的函数,将注册到Lu系统,以实现自动销毁Simple
{
	Simple *pSimple;

	pSimple=(Simple*)me;
	if(pSimple->bDelete==false)		//如果用户忘记了delete
	{
		pSimple->bDelete=true;
		delete pSimple;		//这是Lu系统自动delete
	}
}
LuData _stdcall OpLock_Simple(luINT m,LuData *Para,void *hFor,int theOperator)	//Simple的运算符重载函数
{
	LuData a;

	a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;	//不对Simple类型的对象做运算,总返回nil
	return a;
}

class SimpleCpp		//编写一个简单的类帮助测试:不使用Lu系统
{
public:
	int number;	//数据

	SimpleCpp(int param=0)
	{
		//初始化代码
		number = param;
	};

	~SimpleCpp()
	{
		//销毁对象的其他代码
	};
};

void main(void)
{
	Simple *pSimple[imax];
	SimpleCpp *pSimpleCpp[imax];
	int i,j;
	clock_t start, end;

	start = clock();
	for(j=0;j<kmax;j++)
	{
		for(i=0;i<imax;i++)
		{
			pSimpleCpp[i]=new SimpleCpp(i);
		}
		for(i=0;i<imax;i++)
		{
			delete pSimpleCpp[i];
		}
	}
	end = clock();
	cout << "--- C/C++ 系统运行时间 : " << end - start << " ms." << endl;

	////////////////////////////////////////////////////////////////////////////

	if(!InitLu()) return;	//初始化Lu

	LockKey(key_Simple,Del_Simple,OpLock_Simple);	//在Lu键树中加锁键,只能存储Simple类型

	start = clock();
	for(j=0;j<kmax;j++)
	{
		for(i=0;i<imax;i++)
		{
			pSimple[i]=NewSimple(i);	//使用专用的生成Simple的函数
		}
		for(i=0;i<imax;i++)
		{
			//delete pSimple[i];
			//通过在Lu键树中删除键值的方法销毁对象,参数1表示先将对象放入缓冲池,由Lu系统择机销毁
			DeleteKey((char *)&(pSimple[i]),sizeof(luVOID),key_Simple,Del_Simple,1);
		}
	}
	end = clock();
	cout << "--- C/C++ Lu 复合系统运行时间 : " << end - start << " ms." << endl;

	LockKey(key_Simple,NULL,OpLock_Simple);	//在Lu键树中解锁键

	FreeLu();		//释放Lu
}


运行结果:

--- C/C++ 系统运行时间 : 234 ms.
--- C/C++ Lu 复合系统运行时间 : 125 ms.

4函数说明

    本例用到了Lu的7个输出函数:初始化Lu的函数InitLu,释放Lu的函数FreeLu,插入键值的函数InsertKey删除键值的函数DeleteKey,加锁键函数LockKey,枚举键值的函数EnumKey,从缓冲池中获取一个对象的函数GetBufObj。从这里查看这些函数的说明:Lu编程指南

5 难点分析

    代码1实现了智能指针。对C/C++程序来说,仅需要在类的构造函数和析构函数中添加几行代码;当然,加载Lu核心库、对指定的数据类型进行加锁和解锁还是必不可少的。

    代码2在代码1的基础上实现了对C/C++程序内存泄露的检查。仅增加了一个枚举键值的函数EnumKey

    代码3对使用Lu智能指针的对象生成及销毁效率进行了测试,并与C/C++做了比较,结果表明:在不使用Lu缓冲池时,C/C++和Lu复合程序的效率约为C/C++程序的1/50左右。

    代码4仍然对使用Lu智能指针的对象生成及销毁效率进行测试,但使用Lu缓冲池,结果表明:在使用Lu缓冲池时,C/C++和Lu复合程序的效率接近C/C++程序的2倍。使用Lu缓冲池需要付出的代价是,你必须专门设计一个生成对象的函数来代替new操作符,销毁对象时也不要直接delete,而要使用Lu系统的函数DeleteKey。另外,代码4在小范围内进行了对象的生成及销毁的测试(对象数目 imax=10),充分利用了Lu缓冲池,这是符合一般程序运行情况的;如果修改代码4中的 imax=1000kmax=1000(循环次数)再进行测试,将会发现,C/C++和Lu复合程序的效率将退化为代码3的情况。

6 其他

    你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。


版权所有© Lu程序设计 2002-2013,保留所有权利
E-mail: forcal@sina.com
  QQ:630715621
最近更新: 2014年01月03日

Re:CCNA_CCNP 思科网络认证 动态路由 EIGRP OSPF 协议======================# EIGRP协议特点(CISCO产品专用独家协议) 使用Hello消息发现邻居,然后交换路由信息,使用Hello包维持邻居表 代替其它动态协议周期性更新而消耗资源。 有备用路径,当最佳路径不可用,立即使用备用路径 备用路径比动态获取新路径效率更高。 度量值默认为带宽延迟,也可以添加负载可靠性以及最大传输单元(MTU) rip只是hops跳数为依据,使用带宽延时为指标更合理 还可以负载、可靠性MTU为依据,选择最佳路径。 默认支持4条链路的不同代价的负载均衡,可以更改为最多6条 最大跳数为255(默认是100跳) rip只有15hops,所有只能够使用在小型网络中。 触发式更新路由表,即网络发生变化时,增量更新 hello包触发式结合,消耗设置资源更低 支持路由的自动汇总。 支持大的网络,可以使用自制系统号来区别可共享路由信息的路由器集合,路由信息只可以在拥有相同自制系统号的路由器间共享。 (即一片路由另一片路由,不计划发布沟通的情况下,可以以系统号区分) 如同VLAN方式 管理距离是90 直连0静态路由1;rip协议120;EIGRP协议90(比rip优先级高) # EIGRP度量值 EIGRP度量值 带宽 延迟 可靠性 负载 最大路径跳数 默认支持4条等价路径 最大跳数100,也可以设置成255 # EIGRP三张表 邻居关系表 拓扑表 路由表 # EIGRP专业术语 可行距离(FD)                :A到E最小开销的路径(最佳路径) 被通告距离(AD)            :A的前一个路由器,到E的开销 继任者(最佳路径)          :可行路径下一跳的路由器 可行的继任者(备用路径):被通告距离 ---------------------------------------------------------------------------------------# 介绍OSPF协议 开放最短路径优先(OSPF)是一个开放标准的路由选择协议,它被各种网络开发商所广泛使用。 即无厂家边界 # OSPF协议具有下列特性: 由区域自治系统组成 最小化的路由更新的流量(触发式更新,平时hello包打招呼,类eigrp协议) 允许可缩放性 支持变VLSMCIDR(五类间路由/23) 拥有不受限的跳数 允许多销售商的设备集成(开放的标准) 度量值是带宽 # OSPF术语 Router-ID(网络中的身份:取ip最大值) 网络中运行OSPF协议的路由器都要有一个唯一的标识,这就是Router-ID,并且Router-ID在网络中绝对不可以有重复。 COST(开销) OSPF协议选择最佳路径的标准是带宽,带宽越高计算出来的开销越低。到达目标网络的各个链路累计开销最低的,就是最佳路径。 链路(Link) 就是路由器上的接口,在这里,应该指运行在OSPF进程下的接口。 链路状态(Link-State) 链路状态(LSA)就是OSPF接口上的描述信息,例如接口上的IP地址,子网掩码,网络类型,Cost值等等,OSPF路由器之间交换的并不是路由表,而是链路状态(LSA)。 邻居(Neighbor) 两台或多台运行OSPF的路由器在一个公共的网络上形成的基本关系。 但是不一定交换信息 邻接(Adjacency) OSPF只有邻接状态才会交换LSA。 只有发生交换数据关系的设备间叫做邻接 邻居间选择一个交通站DR,负责邻居间交换数据--------------------------------------------------------------------------------------- # 在边界路由器通过再发布方式向内部网段传递默认路由 两个不同协议自治区:RIP EIGRP 路由再发布 两个不同协议自治区:OSPF EIGRP 路由再发布 两个不同协议自治区:OSPF RIP 路由再发布------------------------------------------------------------------                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值