带动态数组的DELPHI类与SetLength()有问题

时间:2011-07-26 16:08:06

标签: 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;

在实现中,尝试调整Fruit对象数组或初始化为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;

如何在类中使用动态数组?

3 个答案:

答案 0 :(得分:13)

替换

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

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

然后它有效。

答案 1 :(得分:9)

有些东西告诉我你忽略了创建TObjectTree的实例。你已经声明了一个TObjectTree变量,但是你没有调用TObjectTree.Create,或者你直接在你声明的变量上调用它而不是为该变量赋值:

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

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

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

答案 2 :(得分:6)

作为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<TFruit>