看看这段代码,是否会存在内存泄漏的问题! ( 积分: 20 )

  • 主题发起人 主题发起人 searoom
  • 开始时间 开始时间
S

searoom

Unregistered / Unconfirmed
GUEST, unregistred user!
function TUserMaint.GetDepList: TStrings;
var
i: Integer;
tmpstrs: TStrings;
begin
tmpstrs:=TStringlist.Create;

with UserDM do
try
if not adcUser.Connected then
adcUser.Connected:=True;
adqTemp.sql.Clear;
adqTemp.sql.add('select * from M_BMBM');
adqTemp.Open;
for i:=1 to adqTemp.RecordCount do
begin
tmpstrs.Add(adqTemp.Fieldbyname('BMMC').AsString);
adqTemp.Next;
end;
adqTemp.Close;
result:=tmpstrs;
finally
adcUser.Connected:=False;
end;
end;

就是在函数内部建立对象,然后将该对象作为结果返回

如果引用该函数的过程没有释放该结果怎么办?!
 
function TUserMaint.GetDepList: TStrings;
var
i: Integer;
tmpstrs: TStrings;
begin
tmpstrs:=TStringlist.Create;

with UserDM do
try
if not adcUser.Connected then
adcUser.Connected:=True;
adqTemp.sql.Clear;
adqTemp.sql.add('select * from M_BMBM');
adqTemp.Open;
for i:=1 to adqTemp.RecordCount do
begin
tmpstrs.Add(adqTemp.Fieldbyname('BMMC').AsString);
adqTemp.Next;
end;
adqTemp.Close;
result:=tmpstrs;
finally
adcUser.Connected:=False;
end;
end;

就是在函数内部建立对象,然后将该对象作为结果返回

如果引用该函数的过程没有释放该结果怎么办?!
 
如果引用函数没有释放,比如这样:
for i:=0 to 10000 do GetDepList;
肯定会引起内存泄漏。

 
用BoundsChecker检查一下就知道了[:)]
 
应该不会造成内存陷漏,不过首先你应该保证接受者最后要释放掉,即
Strs:=GetDepList;最后必须将Strs释放掉,
因为在执行过程中,GetDepList将自身的tmpstrs的地址传给Strs,然后Strs将自己的地址指向tmpstrs,在最后释放Strs的时候,内存内的对象亦会被释放掉。
不过这样会在内存中遗留下来4字节的内存,这个可以在你的方法的finally中将tmpstrs的指针赋值为空,tmpstrs:=nil
个人愚见,多多指正[:)]
 
他是这样使用这个函数的:

dbcbDep.Items.AddStrings(objUsers.GetDepList);

这是刘艺的代码,我总觉得这样做好象有问题
显然,AddStrings 并不是 "=&quot
,它是没有将 obj.Users.GetDepList 的结果释放的
 
会有泄漏的,tmpstrs:=TStringlist.Create;后没有free;
除非在AddStrings后有对其释放的代码
 
但是这些可是 刘艺 的书本中的代码哦,不知道怎么回事
 
会有问题的
返回值为对象的方法,必须由调用者自己释放内存,不管使用这个返回对象的对象什么的有没释放它,自己再Free一遍是应该的。
而且这个方法中没有防错处理,如果Create之后出错了怎么办?也会内存泄漏的说。
 
会有问题的:
修改如下:
function TUserMaint.GetDepList: TStrings;
var
i: Integer;
//tmpstrs: TStrings;
begin
//tmpstrs:=TStringlist.Create;
Result := TStringlist.Create

with UserDM do
try
if not adcUser.Connected then
adcUser.Connected:=True;
adqTemp.sql.Clear;
adqTemp.sql.add('select * from M_BMBM');
adqTemp.Open;
for i:=1 to adqTemp.RecordCount do
begin
//tmpstrs.Add(adqTemp.Fieldbyname('BMMC').AsString);
Result.Add(adqTemp.Fieldbyname('BMMC').AsString);
adqTemp.Next;
end;
adqTemp.Close;
//result:=tmpstrs;
finally
adcUser.Connected:=False;
end;
end;
使用:
Var
ResultList : TStringList;
begin
ResultList := XXX.GetDepList

end;

 
to 网事如风

您的代码好象没变化吧,只不过是建立 result 而已,不一样吗?
 
dbcbDep.Items.AddStrings(objUsers.GetDepList);
查看AddStrings的代码,跟踪到最后是TStringList的InsertItem
procedure TStringList.InsertItem(Index: Integer
const S: string
AObject: TObject);
begin
Changing;
if FCount = FCapacity then Grow;
if Index &lt
FCount then
System.Move(FList^[Index], FList^[Index + 1],
(FCount - Index) * SizeOf(TStringItem));
with FList^[Index] do
begin
Pointer(FString) := nil;
FObject := AObject;
FString := S;
end;
Inc(FCount);
Changed;
end;
可以看到他最后还是进行了赋值操作
另外,对于GetDepList()方法,在最后指针置空就可以了,至于他的对象,就有调用者(dbcbDep)释放就可以了
 
to Endy.Vee

在 InsertItem 之前,AddStrings 是将其参数中的数据一个个取出来的
但是并没有将他的参数的那个 TStrings 对象释放掉(而且也不应该释放掉,否则破坏了类本身的独立性)

我想,刘艺这样使用代码(dbcbDep.Items.AddStrings(objUsers.GetDepList);
),而且程序本身也并没有出现异常情况(内存是否丢失了就不大清楚),是不是函数返回的对象不用去理他的?

可是这样好象又不符合常理
 
嗬嗬,好像是由内存泄漏阿,就是那4个字节了,它确实没有释放,程序也不会自动释放,不好意思,我会错意了[:D]
 
function TUserMaint.GetDepList: TStrings;
var
i: Integer;
tmpstrs: TStrings;
begin
tmpstrs:=TStringlist.Create;
with UserDM do
try
if not adcUser.Connected then
adcUser.Connected:=True;
adqTemp.sql.Clear;
adqTemp.sql.add('select * from M_BMBM');
adqTemp.Open;
for i:=1 to adqTemp.RecordCount do
begin
tmpstrs.Add(adqTemp.Fieldbyname('BMMC').AsString);
adqTemp.Next;
end;
adqTemp.Close;
finally
adcUser.Connected:=False;
end;
[red]result:=tmpstrs;[/red]//该语句在原位置不正确,其存在内存泄漏,放此处才正确。
end;

如果引用该函数的过程没有释放该结果怎么办?!
如果引用该函数的过程没有释放该结果那么也将导致内存泄漏。
一般解决方法采取外部分配对象的方法(外部分配对象,一般不会忘记释放,并且易于查出泄漏)
上述程序改写如下:

procedure TUserMaint.GetDepList(tmpstrs: TStrings);
var
i: Integer;
begin
with UserDM do
try
if not adcUser.Connected then
adcUser.Connected:=True;
adqTemp.sql.Clear;
adqTemp.sql.add('select * from M_BMBM');
adqTemp.Open;
for i:=1 to adqTemp.RecordCount do
begin
tmpstrs.Add(adqTemp.Fieldbyname('BMMC').AsString);
adqTemp.Next;
end;
adqTemp.Close;
finally
adcUser.Connected:=False;
end;
end;


 
同意楼上的
用传入对象的方法来取值
一般就不会忘记释放了
建议用这种方法。。。
 
to abookdog

谢谢你的意见。我以前也是一直这样做的,就是从外部传入对象在函数内部使用
可是看到一些函数使用在函数内部建立然后返回对象的处理方法,总觉得不妥,而好象也没有人来质疑,所以觉得奇怪。而且连刘艺的书本都是这样处理的。

所以我提出这个疑问,看看大家的理解,看来我的理解和大家应该差不多
就是不知道是否该给刘艺提个醒
 
其实,在函数内部建立对象并返回本没有大错,可是,很难保证使用该函数的人能够去主动释放这个结果的(除非像 Create 这样的比较明确的函数)

所以我总认为返回函数内部建立的对象是不妥的(除非像构造函数这样的特殊函数)
 
该tstrings后来好像付值给一个listbox了。然后有listbox处理此tstrings了。所以放在刘艺老师的例子中,就不存在内存泄漏问题了。如果你单独拿出来用,那就存在内存泄漏问题了。如果你用完后,要有释放动作的,不管在那里都要释放,就行了。
 
function TUserMaint.GetDepList: TStrings;
var
i: Integer;
tmpstrs: TStrings;
begin
tmpstrs:=TStringlist.Create;

try //加上

with UserDM do
try
if not adcUser.Connected then
adcUser.Connected:=True;
adqTemp.sql.Clear;
adqTemp.sql.add('select * from M_BMBM');
adqTemp.Open;
for i:=1 to adqTemp.RecordCount do
begin
tmpstrs.Add(adqTemp.Fieldbyname('BMMC').AsString);
adqTemp.Next;
end;
adqTemp.Close;
result:=tmpstrs;
finally
adcUser.Connected:=False;
end;
// 下面也是加上的
except
tmpstrs.Free;
Raise;
end;
///////////////////////////////////////
end;

记得传出去后最后也得释放返回StringList
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
937
SUNSTONE的Delphi笔记
S
G
回复
4
查看
597
guoleimail
G
后退
顶部