Delphi:使用System.New()而不是局部变量有什么好处,除了节省少量内存?

时间:2013-01-09 05:22:20

标签: delphi pascal fpc

让我们回到基础。坦率地说,我之前从未使用过NewDispose函数。但是,在我在the New() documentation and the included examples的网站和Embarcadero Technologies上阅读Delphi Basics explanation of New()之后,它会在我脑海中留下问题:

使用System.New()而不是局部变量有什么好处,除了备用微量内存?

New()的常见代码示例或多或少如下:

  var
      pCustRec : ^TCustomer;
  begin
      New(pCustRec);
      pCustRec^.Name := 'Her indoors';
      pCustRec^.Age  := 55;
      Dispose(pCustRec);
  end;

上述代码在什么情况下比下面的代码更合适?

  var
      CustRec : TCustomer;
  begin
      CustRec.Name := 'Her indoors';
      CustRec.Age  := 55;
  end;

6 个答案:

答案 0 :(得分:12)

如果您可以使用局部变量,请执行此操作。这是一条规则,几乎没有例外。这导致了最干净,最有效的代码。

如果需要在堆上进行分配,请使用动态数组,GetMem或New。分配记录时使用新建。

无法使用堆栈的示例包括在编译时未知大小的结构或非常大的结构。但对于作为New的主要用例的记录,这些问题很少适用。

因此,如果你面对记录的堆栈与堆的选择,那么堆栈总是正确的选择。

答案 1 :(得分:7)

本地静态变量在有限堆栈上保留空间。分配的内存位于堆上,基本上所有内存都可用。

如上所述,堆栈空间有限,因此您应该避免使用大型局部变量以及通过值传递的大型参数(参数声明中缺少var / const)。

关于内存使用的一个词:
1.简单类型(integercharstringdouble等)直接位于堆栈上。使用的字节数可以由sizeof(variable)函数确定 2.这同样适用于记录变量和数组。 3.指针和对象需要4/8字节。

答案 2 :(得分:7)

从不同的角度来看:

两者都会受到buffer overflow and can be exploited的影响。

如果局部变量溢出,则会得到stack corruption

如果堆变量溢出,则会得到heap corruption

有人说堆栈损坏比堆损坏更容易被利用,但是that is not true in general

请注意,操作系统,处理器体系结构,库和语言中有各种机制可以帮助防止出现这类漏洞。

例如,DEP(Data Execution Prevention),ASLR(Address Space Layout Randomization)和more are mentioned at Wikipedia

答案 3 :(得分:4)

每个对象(即类实例)总是在堆上分配。

可以在堆上分配值结构(简单的数字类型,仅包含这些类型的记录)。

动态数组和字符串内容始终在堆上分配。只能在堆栈上分配引用指针。如果你写:

  function MyFunc;
  var s: string;
  ...

这里,堆栈上分配了4/8个字节,但字符串内容(文本字符)将始终在堆上分配。

因此使用New() / Dispose()效果不佳。如果它不包含引用计数类型,则可以改为使用GetMem() / FreeMem(),因为没有内部指针设置为零。

New()Dispose()的主要缺点是,如果发生异常,您需要才能使用try ... finally块:

  var
    pCustRec : ^TCustomer;
  begin
    New(pCustRec);
    try
      pCustRec^.Name := 'Her indoors';
      pCustRec^.Age  := 55;
    finally
      Dispose(pCustRec);
    end;
  end;

然而,在堆栈上分配让编译器以隐藏的方式为您完成:

  var
    CustRec : TCustomer;
  begin // here a try... is generated
    CustRec.Name := 'Her indoors';
    CustRec.Age  := 55;
  end;  // here a finally + CustRec cleaning is generated

这就是为什么我几乎从不使用New() / Dispose(),而是在堆栈上分配,甚至在类中更好。 2

答案 4 :(得分:2)

堆分配的通常情况是,对象必须比创建它的函数寿命更长:

  1. 它作为函数结果或通过var / out参数返回,可以直接返回,也可以返回一些容器。

  2. 它存储在某个对象,结构或集合中,该对象,结构或集合在过程中传入或以其他方式可访问(这包括发信号/排队到另一个线程)。

答案 5 :(得分:1)

如果堆栈空间有限,您可能更喜欢从堆中分配 Ref.