不是什么时候都可以用栈来声明对象并使用(自动释放)——Delphi里到处都是编译器魔法,并且自动帮助实例化界面元素指针

时间:2021-01-05 16:06:03

一直都喜欢这样显示窗口,因为简单高效:

void MainWidget::ShowMyWindow()
{
MyWidget form(
this);
form.resize(
760,611);
form.exec();
}

今天忽然想到一个问题,栈的空间只有1M或者2M,一个窗口如果数据比较多的话,就装不下,这时候就不能使用栈来声明、使用和销毁对象了。所以就要这样写:

void MainWidget::ShowMyWindow()
{
MyWidget
* form;
form
= new MyWidget(this);
form
->resize(760,611);
form
->exec();
delete form;
}

因为好奇,所以动手实测了一下:

void MainWidget::ShowMyWindow()
{
Util::ShowMessage(
sizeof(MyWidget));
}

这个窗口也就112字节,离1024还比较远。但那也是因为所有界面元素都是动态生成的,所有界面元素在.h文件里全部都是指针声明。否则的话,那也是不行的。

后来又测试了主窗口MainWidget,也就160,看来是够用了。

但是我特别注意到,像QSharedMemory这样真正用来记录数据的东西,不应该在栈上建立实例,否则很可能会堆栈溢出。

------------------------------------------------------------------------

题外话,Delphi窗体里虽然有众多的界面元素,但通过编译器魔法,其实也都是指针而已。当TForm.Create(self)的时候,实际上VCL帮你把所有界面元素从dfm文件中读出数据,然后自动初始化(生成实例)了:

procedure TForm1.Button1Click(Sender: TObject);
begin
FrmFinance:
=TFrmFinance.Create(self);
with FrmFinance do
try
InitForm(strNumero);
ShowModal;
finally
Free;
end;
end;

这么重要的思想到今天才认识到,感觉有点失败。。。Delphi程序员们真是温室里的花朵(我是其中一朵),生在福中而不至福啊!!

为了证实这一点,现在做个小实验,新建一个项目,上面只放一个按钮Button1,然后输入以下代码:

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(sizeof(TForm1)));
ShowMessage(IntToStr(sizeof(TButton)));
ShowMessage(IntToStr(sizeof(self)));
end;

结果这三个值都是4,这其实是三个指针的长度值,真是活生生领略了Delphi的“编译器魔法”,C++就不这样,sizeof可以直接评估类所占用的内存大小。

不过没关系,Delphi也有自己的过墙梯:

procedure TForm1.Button1Click(Sender: TObject);
var
myform : TForm;
begin
myform :
= TForm.Create(self);
ShowMessage(IntToStr(myform.InstanceSize));
// 760
ShowMessage(IntToStr(button1.InstanceSize)); // 536
ShowMessage(IntToStr(self.InstanceSize)); // 764
end;

结果发现,三个值分别是760、536和764。这里特别注意第三个值,是myform的内存长度(760)+Button1指针的内存长度(4),而不是myform(760)+Button1实例的长度(536)。如果再增加一个TMemo,那么form1的长度就差成了760+2*4=768,实测无误。

既然如此,请问这个Button1是什么时候实例化对象的呢?答案就是读取dfm的时候,具体哪一句语句?我估计是这句:

implementation

{$R *.dfm}

其实这还不是一句编程的语句,所以什么内容都看不到,只能等有时间研究Lazarus的实现代码了。