gpt4 book ai didi

arrays - Delphi:如何从类中引用数组

转载 作者:行者123 更新时间:2023-12-05 08:48:41 25 4
gpt4 key购买 nike

我使用数组并测试了类上下文中的功能,例如:

Ttest   = class
values : array of integer;
procedure doStuff;
end;

doStuff 等方法都对值数组进行操作,而无需将数组作为参数传递。这很适合我,而且速度很快。现在我想使用这个类来处理外部数组,比如 Ttest.create(myValues) 在构造函数中我可以将 myValues 复制到内部 values 但这将是一种浪费,而且最后必须反转副本以将更新的值传回。我的问题是如何扩展此类,以便它可以有效地使用外部数组。在这样的伪代码中:

constructor create(var myValues : array of integer);
begin
address of values := address of myValues;
doSTuff;
end;

最佳答案

第 1 课

在德尔福中,dynamic arrays是引用类型。动态数组类型的变量只包含指向实际动态数组堆对象的指针,并且在赋值中,

A := B

哪里AB是相同类型的动态数组,不会复制动态数组堆对象。唯一发生的事情是 AB将指向相同的动态数组堆对象(即,32 位或 64 位 B 指针被复制到 A )并且 reference count堆对象的增加 1.

第 2 课

当你写作时

constructor Create(var AValues: array of Integer);

你需要意识到,尽管看起来如此,但这并不是一个动态数组参数,而是一个open array parameter。 .

如果你明确想要一个动态数组参数,你需要明确地使用这样的类型:

constructor Create(AValues: TArray<Integer>);

根据定义,TArray<Integer> = array of IntegerInteger 的动态数组

请注意,该语言只有两种类型数组——静态和动态;开放数组概念仅与函数参数有关。

如果您想使用动态数组,利用它们作为引用类型的特性,我建议您使用动态数组参数。然后唯一传递给函数(在本例中为构造函数)的是指向堆对象的指针。当然,堆对象的引用计数也会增加。

第 3 课 – 示例

var
Arr: TArray<Integer>;

procedure Test(A: TArray<Integer>);
var
i: Integer;
begin
for i := Low(A) to High(A) do
A[i] := 2*A[i];
end;

procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin

SetLength(Arr, 10);
for i := 0 to High(Arr) do
Arr[i] := i;

Test(Arr);

for i := 0 to High(Arr) do
ShowMessage(Arr[i].ToString);

end;

SetLength 之后, Arr指向引用计数为 1 的动态数组堆对象。您可以在 RAM 中看到它(按 Ctrl+Alt+E,然后 < kbd>Ctrl+G 并转到 Arr[0] )。当您输入 Test ,引用计数增加到 2,因为 ArrA引用它。当你离开时Test ,引用计数减少回 1 因为 A超出范围:现在再次只有Arr指的是它。

第 4 课

现在,一个“陷阱”:如果您更改动态数组的元素数量,则需要重新分配它 (*)。因此,创建了一个引用计数为 1 的新动态数组堆对象,旧对象的引用计数减 1(如果变为零则删除)。

因此,虽然前面的示例按预期工作,但以下不会:

var
Arr: TArray<Integer>;

procedure Test(A: TArray<Integer>);
var
i: Integer;
begin
SetLength(A, 5);
for i := Low(A) to High(A) do
A[i] := 2*A[i];
end;

procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin

SetLength(Arr, 10);
for i := 0 to High(Arr) do
Arr[i] := i;

Test(Arr);

for i := 0 to High(Arr) do
ShowMessage(Arr[i].ToString);

end;

SetLength将创建一个新的引用计数为 1 的动态数组堆对象,并将旧数组的一半复制到其中,将新地址放在本地 A 中参数,和 Test将转换这个新数组,不触及全局 Arr 的旧数组。变量指向。原堆对象的引用计数减一。

但是,如果您使用 var参数,

procedure Test(var A: TArray<Integer>);
var
i: Integer;
begin
SetLength(A, 5);
for i := Low(A) to High(A) do
A[i] := 2*A[i];
end;

它将像以前一样工作。这有效地适用于 Arr .如果我们增加了元素的数量,数组可能会被重新分配并且全局 Arr变量将用新地址更新。

结论

只要你不需要重新分配内存(改变元素的数量),Delphi 已经给了你你想要的,因为动态数组是引用类型。如果您确实需要重新分配,至少现在您了解足够的技术细节以便进行推理。

更新:因此,建议是做

type
TTest = class
FData: TArray<Integer>;
constructor Create(AData: TArray<Integer>);
procedure Enlarge;
procedure Shrink;
procedure ShowSum;
end;

{ TTest }

constructor TTest.Create(AData: TArray<Integer>);
begin
FData := AData; // will NOT copy the array, since dynamic arrays are reference types
end;

procedure TTest.Enlarge;
var
i: Integer;
begin
for i := 0 to High(FData) do
FData[i] := 2*FData[i];
end;

procedure TTest.ShowSum;
var
s: Integer;
i: Integer;
begin
s := 0;
for i := 0 to High(FData) do
Inc(s, FData[i]);
ShowMessage(s.ToString);
end;

procedure TTest.Shrink;
var
i: Integer;
begin
for i := 0 to High(FData) do
FData[i] := FData[i] div 2;
end;

测试它:

procedure TForm1.FormCreate(Sender: TObject);
var
MyArray: TArray<Integer>;
t: TTest;
begin

MyArray := [1, 2, 3, 4, 5];

t := TTest.Create(MyArray);
try
t.ShowSum;
t.Enlarge;
t.ShowSum;
t.Shrink;
t.ShowSum;
finally
t.Free;
end;

end;

脚注

  • 如果您 (1) 减少元素的数量并且 (2) 引用计数为 1,则数据通常不会在内存中移动。如果引用计数 > 1,则始终移动数据,因为 SetLength 保证其参数的引用计数为 1当它返回时。

关于arrays - Delphi:如何从类中引用数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65595809/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com