我们假设我有以下自定义列表,其中包含以下声明:
type
TCustomList = class(TObjectList)
private
function GetItem(AIndex: Integer): TMyObject; // virtual;
procedure SetItem(Index: Integer; AObject: TMyObject);
...
public
property Items[Index: Integer]: TMyObject read GetItem write SetItem;
procedure InsertSort();
end;
Run Code Online (Sandbox Code Playgroud)
通过以下实现:
implementation
function TCustomList.GetItem(AIndex: Integer): TMyObject;
begin
Result := TMyObject(inherited Items[AIndex]);
end;
procedure TCustomList.SetItem(Index: Integer; AObject: TMyObject);
begin
inherited Items[Index]:= AObject;
end;
procedure TCustomList.InsertSort;
var
i, j: integer;
key: TMyObject;
begin
for j := 1 to self.Count - 1 do
begin
key:= self.Items[j];
i := j - 1;
while ((i >= 0) AND (self.Items[i].Compare(key)>0)) do
begin
self.Items[i+1]:= self.Items[i]; // does not WORK!!! properly.. System.Contnrs problem ??
i := i-1;
end; // while
self.Items[i+1]:= key;
end; // for
end; // procedure InsertSort
Run Code Online (Sandbox Code Playgroud)
当我在一组实例上运行代码时TMyObject
,我得到一个无效的指针操作异常.这一点,我相信,通过阅读和较差的元素书写造成TCustomList
通过Items
性能.
为什么会出现这个无效指针操作异常?
这里发生的事情是对象列表的所有权正在阻碍.因为您正在使用TObjectList
,所以当列表被要求忘记某个成员时,它会将其销毁.当您编写代码时会发生这种情况:
self.Items[i+1] := ...
Run Code Online (Sandbox Code Playgroud)
i+1
在分配之前存储在索引处的成员将被销毁,以便为新项目腾出空间.最终,您将最终销毁已经被销毁的对象,并且当您的无效指针异常发生时.
要解决此问题,可以使用Extract
允许您删除项目而不破坏它的方法.或者@Arioch在评论中巧妙地指出,这种Exchange
方法非常适合比较.
更容易的是在排序期间暂时切换OwnsObjects
到,False
在完成后不要忘记恢复它.或许你甚至不打算使用OwnsObjects=True
.在那种情况下,你想要TList
.
坦率地说,尽管使用Sort
最初暴露的内置方法会好得多TList
.你根本没有必要在已经有一个非常体面的类上实现一个排序方法.