请勿拍我,但这是我第一次看到使用本地可写 const
(或者我可能只是太老了......):{ {3}}
查看本地const FullScreen: Boolean = False;
然后FullScreen := not FullScreen;
起初我认为这是现代Delphi版本的新功能,但它也适用于我的D5。所以我的问题是:本地可写常量与声明全局可写常量完全相同吗?
e.g。
procedure TForm1.Button1Click(Sender: TObject);
Const
LocalConst: Boolean = False;
begin
LocalConst := not LocalConst;
if LocalConst then Beep;
end;
像这段代码一样工作吗? :
Const
GlobalConst_Button2Click: Boolean = False;
procedure TForm1.Button2Click(Sender: TObject);
begin
GlobalConst_Button2Click := not GlobalConst_Button2Click;
if GlobalConst_Button2Click then Beep;
end;
或者,LocalConst
是本地的方法,即静态?这个恒定的线程是否安全?
任何人都可以对这个问题有所了解吗?
答案 0 :(得分:11)
具有本地和全局类型常量的代码完全相同。
正如大卫已经说过的那样,整个程序中可以访问全局静态变量(也称为类型常量),而本地静态变量则不可以。
下面我将typed constants
称为static variables
,因为这就是他们真正的目标。
然而,本地静态变量确实以与全局静态var完全相同的方式在内存中保留。只是编译器不允许您从例程外部访问本地var 还要注意,如果你的本地静态var特别大(或者你有很多)它们会占用一块恒定的内存(即使我无法想象这可能是一个问题)
因为静态变量驻留在固定位置,所以它不是线程安全的。 它有效地变成了所有线程实例之间的共享变量 如果静态var不能在单个CPU周期中更改(即,如果它大于整数或者它是复杂类型),则两个线程可以同时更改变量的不同部分,通常会导致损坏。
它可以在一个循环中改变,例如一个布尔或整数,你永远不知道你所在的线程是最后一个更改的线程还是另一个线程,这在大多数情况下会导致不可预测的结果。
简而言之
除非您确切知道自己在做什么,否则在线程代码中使用静态变量是一个非常糟糕的主意。
此例外可能是一个整数计数器,您只需递增并测试以查看是否发生了多于x次的执行。
静态变量通常不适合在线程之间传递消息。
如果您想在线程之间共享数据,最好使用threadvar
,
见:http://en.wikipedia.org/wiki/Thread-local_storage
并且:https://stackoverflow.com/search?q=delphi+threadvar
<强>最后强>
很少有问题需要全局静态变量,因此它们是危险的,因此最好避免使用它们
本地静态变量在单线程代码中非常有用,可以跟踪例程的不同执行之间的状态。
由于竞争条件,它们在多线程代码中执行此操作毫无用处。
答案 1 :(得分:4)
本地可写常量是否与声明全局可写常量完全相同?
唯一的区别是范围。全局变量是全局变量,局部变量具有局部范围。可写类型常量是C静态局部变量的合理近似值。
可写类型常量的巨大缺点是没有像C中那样的关键字支持,你必须使用编译器选项来切换语言的含义!在我看来,这使得可写类型常量实际上无用。
答案 2 :(得分:4)
我参加派对有点晚了,但我仍想在可写的常数上添加一些信息。
首先,正如约翰和大卫所说,全局和局部常数的记忆没有差别。
对于那些对可写常量感兴趣的人: 我发现模拟“Promise”功能使函数“Lazy”很有用。 Ofcourse delphi不支持Promises,因此这只是部分有效。
考虑一个函数来计算字符串中的单词数量:
function CountWords(Input: String):Integer;
var
Worker: TStringList;
begin
Worker := TStringList.Create;
Worker.DelimitedText := Input;
Result := Worker.Count;
Worker.Free;
end;
现在想象它在我们的计划中多次被召唤。 TStringList对象将在每次执行时创建并释放,从而执行额外的工作。 你可以通过创建一个全局变量Worker_CountWords来解决这个问题,在程序启动时初始化它并在你的函数中使用它,但看看这个:
function CountWords(Input: String):Integer;
{$J+} //Enable writable constants
const
Worker: TStringList = nil;
{$J-} //Disable writable constants
begin
if Worker = nil then
begin
Worker := TStringList.Create;
//Other Initialization code here
end;
Worker.DelimitedText := Input;
Result := Worker.Count;
end;
这个函数只会创建一次TStringList并在以后使用它,但永远不会释放它(这里有一种缺点)。但是对于可以在应用程序运行期间随时调用的函数,这是一种合适的选择。这可以让你的代码看起来更清洁,如果你... 现在,注意 - 这实际上不是一个承诺,但它实现了类似的结果。你可以用函数调用来做这个(我已经尝试过替换内存中的实际函数了,这是一个非常糟糕的主意,但你可以创建一个const来保存指向函数的指针,它在开始时保存指向初始化函数的指针,之后替换为实际的worker函数,父函数只能调用一个常量保持的函数。我现在想不出一个很好的例子,所以我会让你自己想出一个。
为了修改常量值,也不需要{$ WRITABLECONST ON},你也可以这样做:
procedure DoSomeWork;
const
FirstCall : TDateTime = 0;
begin
if FirstCall = 0 then
PDateTime(@FirstCall)^ := Now;
Writeln(TimeToStr(FirstCall));
//some actual work here
end;
同样的事情适用于函数中的const
参数,因为它们与var
参数完全相同(通过引用传递以避免花时间创建单独的变量),唯一的区别是编译器没有' t允许您正常更改这些值。
P.S。注意const函数参数,因为你可以传递像foo(12)
这样的实际常量,并尝试修改它可能会搞砸了......
答案 3 :(得分:0)
我完全不理解这个问题,所以我会回答所有可能发生的情况:
CountWords
示例中有两个不同:第一个示例在每次调用时创建并释放类实例。第二个只在第一次调用时创建类实例,然后使用它直到程序终止。CountWords
会有什么不同:它在功能上没有区别,但你必须为它编写一个全新的类,然后在它上面初始化它程序启动并在以后使用它。除了对使用单身人士有很多批评(更多信息请参阅维基),所以我不会这样做。 P.S。我对const
函数参数实际上是错误的,事实证明这些东西在不同的delphi版本中表现不同。 Const参数与Delphi 6中的var
参数的作用相同,但在Delphi XE2中,似乎无论如何都会创建局部变量。无论哪种方式,我都不建议弄乱const
函数参数。