开放数组的非引用计数问题这个问题非常难缠,昨天调试了一个晚上才定位到问题。今天用最简单的方式把问题重新模拟一番。
说明:我有两个list,两个list都是存放array of byte类型数据。其中一个list用于获取数据;另外一个list是对传入数据list内容的拷贝。Type
TByteArr = array of Byte; {自定义结构}
TMyList = class
private
F_lstData : TList; // 类型:TList< TByteArr >
private
function GetAData( index : Integer ) : TByteArr;
public
{加入一条数据}
procedure Add( data : TByteArr );
property Items[ index : Integer ] : TByteArr read GetAData;
end; function TMyList.GetAData( index : Integer ) : TByteArr;
begin
Assert( (index >= 0 ) and (index < F_lstData.Count) );
result := TByteArr(F_lstData.Items[i]); // (1)
end;
procedure TMyList.Add( data : TByteArr );
var
duplicate : TByteArr;
begin
duplicate := Copy(data); // (2) 说明,不使用开放数组引用计数,我自己做拷贝
F_lstData.Add( duplicate );
end;
// 应用程序
var
lst : Tlist;
ml : TMyList;
bytes1 , bytes2 : TByteArr;
peek : TByteArr;
i : Integer;
begin
SetLength( bytes1, 10 );
for i := 0 to High(bytes1) do
bytes1[i] := i;
SetLength( bytes2, 7 );
for i := 0 to High(bytes2) do
bytes2[i] := 2; lst.Add( bytes1 );
lst.Add( bytes2 );
Showmessage( IntToStr( length(bytes1) ) ); // 显示10 for i := 0 to lst.count-1 do
ml.Add(TByteArr(lst.Items[i])); peek := ml.Items[0]; // (3) 得到的值总是越界的!!!!
Showmessage( IntToStr( length(peek) ) ); lst := Tlist.Create;
lst.Free;
ml.Free;
end;
说明:我有两个list,两个list都是存放array of byte类型数据。其中一个list用于获取数据;另外一个list是对传入数据list内容的拷贝。Type
TByteArr = array of Byte; {自定义结构}
TMyList = class
private
F_lstData : TList; // 类型:TList< TByteArr >
private
function GetAData( index : Integer ) : TByteArr;
public
{加入一条数据}
procedure Add( data : TByteArr );
property Items[ index : Integer ] : TByteArr read GetAData;
end; function TMyList.GetAData( index : Integer ) : TByteArr;
begin
Assert( (index >= 0 ) and (index < F_lstData.Count) );
result := TByteArr(F_lstData.Items[i]); // (1)
end;
procedure TMyList.Add( data : TByteArr );
var
duplicate : TByteArr;
begin
duplicate := Copy(data); // (2) 说明,不使用开放数组引用计数,我自己做拷贝
F_lstData.Add( duplicate );
end;
// 应用程序
var
lst : Tlist;
ml : TMyList;
bytes1 , bytes2 : TByteArr;
peek : TByteArr;
i : Integer;
begin
SetLength( bytes1, 10 );
for i := 0 to High(bytes1) do
bytes1[i] := i;
SetLength( bytes2, 7 );
for i := 0 to High(bytes2) do
bytes2[i] := 2; lst.Add( bytes1 );
lst.Add( bytes2 );
Showmessage( IntToStr( length(bytes1) ) ); // 显示10 for i := 0 to lst.count-1 do
ml.Add(TByteArr(lst.Items[i])); peek := ml.Items[0]; // (3) 得到的值总是越界的!!!!
Showmessage( IntToStr( length(peek) ) ); lst := Tlist.Create;
lst.Free;
ml.Free;
end;
解决方案 »
- TServerSocket控件如果限制连接个数?我想只限制1个连接
- 急:不同分辨率下的界面显示如何处理??
- 怎么实现根据运行时的某一变量的值,决定执不执行某一函数?
- 想对一个含有回车符号的字段进行加密,不知道有什么好的算法,请指教!谢谢!重谢!
- 一个写注册表的代码,大伙进来帮帮忙...
- 对Interbase和oracel编程,怎样根据不同的错误代码写相应的错误提示?
- 寻找 maozefa(之源) !!!!!!!!!!!!!
- 请问在FastReport中如何直接打印DBGrid
- 那里有增强的treeview控件下载呢?要求建立节点的速度要快。
- DELPHI内嵌汇编
- 请教:delphi+word的问题,如下异常...
- IDHTTP问题
begin
F_lstData.Add( data );
end;// (3) 处peek的值为0123456789,ok!!!
问题,我不使用引用计数,自己对开放数组做copy,怎么就不行.
后面使用:
SetLength(duplicate, Length(data));
for i := 0 to High(data) do
duplicate[i] := data[i];
还是不行.看来不是copy的问题.
delphi让人看不透,真是失望!!!
template<class Type>
class shared_ptr
{
Type* m_pointer;
int m_nRefCount; // 引用计数
};让链表单独管理共享指针指向的内存是不行的,因为共享指针本身就不能用链表来存储.
lst : Tlist;//--不知道(也许我多嘴,:-))楼主是否以为在这里已经创建了你所定义的对象的实例了呢?
ml : TMyList;//---此处也是一样。
注:在C++中,确实是你定义时,编译器也就分配内存了,存在了这个实例对象,但在D中不是这样的,在D中,动态对象要由程序员自己来动态创建的。在这里,只是定义了两个对象指针罢了。
呵~~
bytes1 , bytes2 : TByteArr;
peek : TByteArr;
i : Integer;
begin SetLength( bytes1, 10 );
for i := 0 to High(bytes1) do
bytes1[i] := i;
SetLength( bytes2, 7 );
for i := 0 to High(bytes2) do
bytes2[i] := 2;
//-----------------楼主,我看不到你在哪儿创建两个列表类(lst,ml)的对象实例呢?
lst:=tlist.create;
ml:=TMyList.caret;//---就算是这样操作。但楼主自定义的类TMyList中并没有提供构造方法constructor CREATE;也就是说ML的私有域 F_lstData : TList; // 类型:TList< TByteArr >
并非是一个存在的对象,它仍然是一个“悬浮指针”罢了。
lst.Add( bytes1 );
lst.Add( bytes2 );
Showmessage( IntToStr( length(bytes1) ) ); // 显示10 for i := 0 to lst.count-1 do
ml.Add(TByteArr(lst.Items[i])); peek := ml.Items[0]; // (3) 得到的值总是越界的!!!!
Showmessage( IntToStr( length(peek) ) ); lst := Tlist.Create;//---你在这儿创建对象实例做什么用?
lst.Free;
ml.Free;
end;我所说的,仅从楼主的代码所得,不对之处,请楼主自己更正了。
var
list:Tlist;
p,pp:^integer;
pa:array of byte;begin
list:=tlist.Create;
new(p);
try
p^:=10;
list.Add(p);
setlength(pa,10);
FillChar(char(pa[0]),10,'a');
list.Add(pa);
pp:=list.Items[0];
showmessage(inttostr(PP^));
pp:=list.Items[1];
showmessage(inttostr(ord(pa[0])));
finally
dispose(p);
list.free;
end;end;
这是我的测试代码,并不报错的。呵~~,仅供参考。
这里想要说明的是:不能使用TList存储动态数组,这样做非常危险.// 应用程序
var
lst : Tlist;
ml : TMyList;
bytes1 , bytes2 : TByteArr;
peek : TByteArr;
i : Integer;
begin
lst := TList.Create;
ml := TMyList.Create; SetLength( bytes1, 10 );
for i := 0 to High(bytes1) do
bytes1[i] := i;
SetLength( bytes2, 7 );
for i := 0 to High(bytes2) do
bytes2[i] := 2; lst.Add( bytes1 );
lst.Add( bytes2 );
Showmessage( IntToStr( length(bytes1) ) ); // 显示10 for i := 0 to lst.count-1 do
ml.Add(TByteArr(lst.Items[i])); peek := ml.Items[0]; // (3) 得到的值总是越界的!!!!
Showmessage( IntToStr( length(peek) ) ); lst.Free;
ml.Free;
end;这个帖子看来分数没人能得到了.
确实使用TList存储动态数组非常危险,这道题也特别难,但这100分我拿定了。
我将你的类做了一下改造,加入了计数操作,看代码吧
(由于CSDN贴出的代码会变引难看,因此我将两个每两个半角空格替换成了一个全角空格,大家试的时候注意替换回来):TByteArrList类,也即原来的TMyList类:
-----------------------------------------------------------------------
unit ByteArrayList;interface
uses
SysUtils, Classes;Type
TByteArr = array of Byte;TByteArrList = class
private
F_lstData : TList; // 类型:TList< TByteArr >
private
function GetAData( index : Integer ) : TByteArr;
public
constructor Create;
destructor Destroy; override; {加入一条数据}
procedure Add( data : TByteArr );
property Items[ index : Integer ] : TByteArr read GetAData; default;
end;implementation
function TByteArrList.GetAData( index : Integer ) : TByteArr;
begin
Pointer(result) := F_lstData.Items[index];
Inc( PInteger(PChar(result)-8)^ ); // 手工增加计数
end;procedure TByteArrList.Add( data : TByteArr );
begin
F_lstData.Add( Pointer(data) );
Inc( PInteger(PChar(data)-8)^ ); // 手工增加计数
end;constructor TByteArrList.Create;
begin
inherited;
F_lstData := TList.Create;
end;destructor TByteArrList.Destroy;
var
data : TByteArr;
i: integer;
begin
// 需要回收内存
for i:=0 to F_lstData.Count-1 do
begin
Pointer(data) := F_lstData[i];
data := nil; // 借用Delphi的自动回收技术
end;
F_lstData.Free; inherited;
end;end.
-----------------------------------------------------------------------主程序,用的是命令行/控制台方式的,你的ShowMessage都改成了Writeln:
-----------------------------------------------------------------------
program ByteArrayListTest;{$APPTYPE CONSOLE}uses
SysUtils,
ByteArrayList in 'ByteArrayList.pas';procedure main();
var
m1, m2 : TByteArrList;
bytes1 , bytes2 : TByteArr;
peek : TByteArr;
i : Integer;
begin
m1 := TByteArrList.Create; SetLength( bytes1, 10 );
for i := 0 to High(bytes1) do
bytes1[i] := i; SetLength( bytes2, 7 );
for i := 0 to High(bytes2) do
bytes2[i] := 2; Writeln( IntToStr( length(bytes1) ) ); // 显示10
Writeln( IntToStr( length(bytes2) ) ); // 显示7 m1.Add( bytes1 );
m1.Add( bytes2 ); bytes1 := nil; // 马上销毁好调试
bytes2 := nil; // 马上销毁好调试 peek := m1.Items[0];
Writeln( IntToStr( length(peek) ) );
peek := nil; // 马上销毁好调试 m2 := TByteArrList.Create;
m2.Add( m1[0] );
m2.Add( m1[1] );
peek := m2.Items[1];
Writeln( IntToStr( length(peek) ) );
peek := nil; // 马上销毁好调试
m2.Free; m1.Free;
end;begin
main();
write( 'press ENTER to quit...' );
readln;
end.
-----------------------------------------------------------------------输出是:
-----------------------------------------------------------------------
10
7
10
7
-----------------------------------------------------------------------
当然,大家还可以加入更多测试,比如看peek的内容对不对。--
http://agui.net.googlepages.com
mailto: agui.cn @ gmail.com
Inc( PInteger(PChar(data)-8)^ ); // 这句话如何理解,只知道一串的强制类型转化.还有想问的是引用计数的数据存放在哪里,我知道boost库一点皮毛,它里面是用一个refCount的整形变量来存放引用次数的.delphi里它是如何实现的?最后一个问题:
Pointer(data) := F_lstData[i]; // 难道等同于 data := Pointer(F_lstData[i]);谢谢.
1、动态数组实体的格式为:
引用计数: 4bytes
长度: 4bytes // 元素个数
内容: 长度*元素大小2、动态数组变量实际上是一个指针,指向的位置为“内容”开始处;3、之所以用PChar(data)先转型,是因为在Delphi中只有Pchar可以和整数运算,实际上用整数来转型也是可以的: integer(data)。PChar(data)-8 取得计数器的地址,PInteger(PChar(data)-8)^ 将计数器的值取出来,Inc() 当然就是加一了。4、data的类型为 TByteArr (动态数组),按照Delphi的语法要进行数组方式的操作的,比如改变计数值,所以必须要转型为Pointer。另外,data := Pointer(F_lstData[i]) 估计要出编译错(Pointer值赋给TByteArr变量),而且 F_lstData[i] 本身就是 Pointer 类型,不需要转换。如果写成 data := TByteArr(F_lstData[i]) 就会引起一系列内部操作,导致错误。使用Pointer是为了我们自己可以控制不会有其它操作发生。有什么问题可以来邮 agui.cn @ gmail.com