让我们回到基础。坦率地说,我之前从未使用过New
和Dispose
函数。但是,在我在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;
答案 0 :(得分:12)
如果您可以使用局部变量,请执行此操作。这是一条规则,几乎没有例外。这导致了最干净,最有效的代码。
如果需要在堆上进行分配,请使用动态数组,GetMem或New。分配记录时使用新建。
无法使用堆栈的示例包括在编译时未知大小的结构或非常大的结构。但对于作为New的主要用例的记录,这些问题很少适用。
因此,如果你面对记录的堆栈与堆的选择,那么堆栈总是正确的选择。
答案 1 :(得分:7)
本地静态变量在有限堆栈上保留空间。分配的内存位于堆上,基本上所有内存都可用。
如上所述,堆栈空间有限,因此您应该避免使用大型局部变量以及通过值传递的大型参数(参数声明中缺少var
/ const
)。
关于内存使用的一个词:
1.简单类型(integer
,char
,string
,double
等)直接位于堆栈上。使用的字节数可以由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)
堆分配的通常情况是,对象必须比创建它的函数寿命更长:
它作为函数结果或通过var
/ out
参数返回,可以直接返回,也可以返回一些容器。
它存储在某个对象,结构或集合中,该对象,结构或集合在过程中传入或以其他方式可访问(这包括发信号/排队到另一个线程)。
答案 5 :(得分:1)
如果堆栈空间有限,您可能更喜欢从堆中分配 Ref.