假设我有以下自定义列表,其中包含以下声明:
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;
通过以下实施:
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
当我在TMyObject
的实例集合上运行代码时,我得到一个无效指针操作异常。我认为,这是由于TCustomList
通过Items
属性读取和写入元素不佳造成的。
为什么会出现这个无效指针操作异常?
答案 0 :(得分:6)
这里发生的事情是对象列表的所有权正在阻碍。因为您正在使用TObjectList
,所以只要要求列表忘记某个成员,它就会销毁它。当你写下代码时会发生这种情况:
self.Items[i+1] := ...
销毁分配前存储在索引i+1
的成员,以便为新项目腾出空间。最终,您将最终销毁已经被销毁的对象以及发生无效指针异常时的对象。
要解决此问题,可以使用Extract
方法,该方法允许您在不破坏项目的情况下删除项目。或者正如@Arioch在评论中巧妙地指出的那样,Exchange
方法非常适合进行比较。
更容易在排序期间暂时将OwnsObjects
切换为False
,而不会忘记在完成后将其恢复。或许你甚至不想使用OwnsObjects=True
。在这种情况下,您需要TList
代替。
坦率地说,虽然您最好使用最初由Sort
公开的内置TList
方法。你根本不需要在一个已经有一个完美体面的类上实现一个排序方法。