给你举个例子吧.就以字符串为例
AnsiString:标准的字符串,以Nil结尾。
Type
TAnsiString=record
allocSiz: LongWord;//分配的大小
ReferencCount:LongWord;
//引用次数========>治理就是你说的计数器
Length:LongWord;
//字符串长度
Data:array[1..(Length+1)] of AnsiChar;
end;
有人要问为什么长度是1..(Length+1)而不是Length呢?
因为后面还要有一位NULL字符,表示结束。可见AnsiString和C/C++的兼容性也是很好的。
如下代码:
var
k: AnsiString;
p:^LongWord;
begin
k := 'I am a Delphi fan!';
p:=@K[1];
Dec(p,1);
P^:=13;
//k.Length被置为13
showmessage(k);
end;
由此我们会知道ANsiString的效率是很高的,他的信息存储于字符串头中。例如我们要取字符串的长度只须读出
TAnsiString.Length的内容即可。而其c/c++的字符串就不行,取字符串的长度要从头读起直到遇到NULL记录下
读过的字符个数就是长度。可是如果字符串很长效率的差异就显示出来了,比如一个很大的文件?
前面讲过动态数组赋值的问题,它并不为动态数组单独开辟一块空间,而是简单的把指针指向所赋的数组。只有被赋值的
数组在改变时才真正分配给他空间。这样做的好处是在赋值的时候会很快。如果被赋值的数组没有改变那就比直接分配空间
快上许多。另外如果被赋值数组发生改变重新分配空间,只是花费和直接分配空间相同的时间而已。
有些人会想那么如下代码:
var
k,j:AnsiString;
begin
k:'123';
j:='456';
k:=j;
end;
k的空间已经分配,既然k的指针指向了j,那么k原来的空间岂不是浪费,造成了内存垃圾?
前面说过AnsiString也是动态数组的一种,符合动态数组生存期自管理。
但实际的情况是:当把一个指针指向一个字符创空间时,他的引用次数位就加1,
当有指针指向从这个字符串离开时引用次数位就减1,所以当引用次数位为0是就意味着没有人使用了
就自动释放这段空间。这就是我们所说的生存期自管理技术。
Type
//实际分配大小对我们来说意义不大,所以这里我们先不取他
PAnsiString=^TAnsiString;
TAnsiString= record
ReferencCount:LongWord;
//引用次数
Length:LongWord;
//字符串长度
end;
var
k,j: AnsiString;
P:^LongWord;
MyAnsiString
AnsiString;
begin
k:='I love Delphi!';计数器应该为1,因为刚刚分配内存,只有一个使用者
P:=@K[1];
Dec(P,2);
MyAnsiString:=@(p^);
showmessage('现在计数器:'+Inttostr(MyAnsiString^.ReferencCount));
showmessage('长度为:'+Inttostr(MyAnsiString^.Length));
j:=K;//计数器应该加1,因为j的指针指向了他,使用者又多了一个
showmessage('现在计数器:'+Inttostr(MyAnsiString^.ReferencCount));
showmessage('长度为:'+Inttostr(MyAnsiString^.Length));
j:='123';//计数器应该减1,因为j的指针指不再指向他,使用者少了一个
showmessage('现在计数器:'+Inttostr(MyAnsiString^.ReferencCount));
showmessage('长度为:'+Inttostr(MyAnsiString^.Length));
k:=j;//k的指针指向j的内容所在,那么这片数据的计数器再减去一个,即为0。
//然而因该该内存区域已经被自动释放所以这里再去读数的话就是一些随机数据
showmessage('现在计数器:'+Inttostr(MyAnsiString^.ReferencCount));
showmessage('长度为:'+Inttostr(MyAnsiString^.Length));
end;
动态数组的回收机制原理相同,现在明白了为什么动态数组再回收时要给他赋成Nil了吧!变体型等生存期自管理的
类型都是用这种机制来实现的.
总结计数器就是一个标志位,表示一个对象(广义的对象)的引用次数.如果没有引用了也就是计数器为0,就清空(释放)对象