欢迎访问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=1000 及kmax=1000(循环次数)再进行测试,将会发现,C/C++和Lu复合程序的效率将退化为代码3的情况。
6 其他
你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。
版权所有© Lu程序设计 2002-2013,保留所有权利
E-mail: forcal@sina.com QQ:630715621
最近更新: 2014年01月03日