Arrays 带有动态数组的DELPHI类存在SetLength()问题

Arrays 带有动态数组的DELPHI类存在SetLength()问题,arrays,delphi,class,dynamic,Arrays,Delphi,Class,Dynamic,我需要创建一个类,该类包含记录对象的数组,但尝试使用SetLength会引发访问冲突错误 考虑以下示例中的一个带有水果的树对象 type TFruit = record color: string; weight: double; end; type TObjectTree = class Public Fruits: array of TFruit; constructor Create; procedure AddFruit; end; 在实现中,尝试调整

我需要创建一个,该类包含记录对象的数组,但尝试使用SetLength会引发访问冲突错误

考虑以下示例中的一个带有水果的树对象

type
TFruit = record
  color: string;
  weight: double;
end;

type
  TObjectTree = class

  Public
  Fruits: array of TFruit;

  constructor Create;
  procedure AddFruit;
end;
在实现中,尝试调整水果对象数组的大小或初始化为nil时会产生问题

constructor TObjectTree.Create;
begin
    inherited Create;
    Fruits:=nil;              //Raises an error
end;


procedure TObjectTree.AddFruit(FruitColor: string; FruitWeight: integer);
begin
    SetLength(Fruits, Length(Fruits)+1);  //Raises an error (when I comment Fruits:=nil; in the constructor)

    Fruits[Length(Fruits)].color:=FruitColor;      
    Fruits[Length(Fruits)].weight:=FruitWeight;
end;
如何在类中使用动态数组?

替换

Fruits[Length(Fruits)].color:=FruitColor;      
Fruits[Length(Fruits)].weight:=FruitWeight;


然后它就起作用了。

作为对iamjoosy和Rob Kennedy答案的补充,我将这样编码:

procedure TObjectTree.AddFruit(FruitColor: string; FruitWeight: integer);
var
  NewCount: Integer;
begin
  NewCount := Length(Fruits)+1;
  SetLength(Fruits, NewCount);
  Fruits[NewCount-1].color := FruitColor;      
  Fruits[NewCount-1].weight := FruitWeight;
end;
在我看来,只调用一次
Length()
更为清晰

您不需要在构造函数中分配
Fruits:=nil
,因为这是自动发生的。实例化对象时,所有字段都初始化为零。也就是说,
Fruits:=nil
不应引发错误。如果出现这种情况,则可能是由于数组访问超出范围而导致内存损坏

需要指出的另一点是,启用范围检查将导致一个信息错误,该错误可以解释问题。这比依赖访问冲突更有帮助。我不能推荐足够高的范围检查


最后,
SetLength(…,Length(…)+1)
模式通常会导致非常低效的内存使用,并会导致大型列表的性能问题。如果您使用的是Delphi 2009+,我建议您改用
TList

有人告诉我您忽略了创建
TObjectTree
的实例。您声明了一个
TObjectTree
变量,但您没有调用
TObjectTree.Create
,或者您直接对声明的变量调用它,而不是为该变量指定新值:

var
  Tree: TObjectTree;
begin
  // This is wrong.
  Tree.Create;

  // This is right.
  Tree := TObjectTree.Create;

如果未正确实例化
TObjectTree
,则没有有效内存来支持您尝试使用的
Fruits
字段,因此为其赋值会产生错误。

+1这更可能解释发生的情况,而不是我的内存损坏解释完全正确。这导致了内存问题。现在可以实例化一个TreeObject并将水果数组设置为nil(以便稍后通过调用要实现的类方法最终清除添加的水果)。在构造函数中将水果设置为nil是毫无意义的。它已经是零了。我通常对这样的代码使用一个函数,返回一个整数,它是新创建项的索引,然后使用
result:=length(Fruits);坐骨长度(果实,结果+1);水果[结果]。颜色:=水果颜色它确实比+1/-1对我来说更有意义,而且有时为调用者检索新创建的索引很方便,不会造成速度损失。就我的2美分。@Arnaud同意了,尽管我只会使用
TList
,当然它就是这么做的。解释是动态数组是基于0的,运行长度是-1。对,这也是示例中的一个错误。
var
  Tree: TObjectTree;
begin
  // This is wrong.
  Tree.Create;

  // This is right.
  Tree := TObjectTree.Create;