【发布时间】:2016-06-13 06:48:23
【问题描述】:
我有一个使用 C++ Builder 10.1 Berlin 构建的跨平台 C++ 应用程序,并且在理解对象的生命周期处理时遇到了问题,在这种情况下,字符串是在类之外声明的。 我创建了一个新的表单应用程序并添加了一些代码。 cpp 文件如下所示:
#include
#pragma hdrstop
#include "FmrMain.h"
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
const String Hello = "Hello";
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ShowMessage(Hello);
}
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
ShowMessage(Hello);
}
我使用 CLANG 增强型 C++11 编译器 bcc32c 编译它,运行应用程序并再次关闭表单。当 TForm1::FormDestroy 被调用时,Hello 已经被销毁。当我使用经典编译器 bcc32 编译 win32 的代码时,字符串在 FormDestroy 之后被销毁。
有人可以对此进行解释或提供一些关于我必须寻找的主题的信息吗?为什么基于 CLANG 的编译器在这里表现不同?
编辑
当我使用自定义类而不是字符串时,调试起来更容易。
class Foo {
public:
Foo(){};
~Foo(){}
};
Foo A;
//--------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
}
创造与毁灭秩序是这样的。我已经添加了调用堆栈。
bcc32c(CLANG C++11 编译器)
-
创建 Foo
:004052C0 Foo(this=:00400000)
:00405070 __cxx_global_var_init3()
:004052A3 _GLOBAL__I_a()
:00405ab7 ; ~福
:321fa2b7 ; C:\Program Files (x86)\Embarcadero\Studio\18.0\bin\CC32C240MT.DLL
:321fa6ff CC32C240MT.__wstartup + 0xbb -
创建Form1
:004052EC TForm1(this=:00402422, __ctor_flag='\0')
:0085c139 fmx240.@Fmx@Forms@TApplication@CreateForm$qqrxp17System@TMetaClasspv + 0x5d
:0085c349 fmx240.@Fmx@Forms@TApplication@RealCreateForms$qqrv + 0x81 -
摧毁 Foo
:004052D0 ~Foo(this=:0040B7DC)
:0040509E __dtor_A()
:321f6246 CC32C240MT.___call_atexit_procs + 0x52
:321f671c CC32C240MT.___exit + 0x20 -
销毁 Form1
:00405868 ~TForm1(this=:5016E698)
bcc32(经典 borland 编译器)
-
创建 Foo
:00404950 Foo::Foo(this=:00409B74)
:004048A0 STCON0()
:00405727 ;根
:322190f1 ; C:\Program Files (x86)\Embarcadero\Studio\18.0\bin\CC32240MT.DLL > :322193b5 CC32240MT.__wstartup + 0xa5 -
创建Form1
:00404994 TForm1::TForm1(this=:02F2AE20, Owner=:02F39620)
:0095c139 fmx240.@Fmx@Forms@TApplication@CreateForm$qqrxp17System@TMetaClasspv + 0x5d
:0095c349 fmx240.@Fmx@Forms@TApplication@RealCreateForms$qqrv + 0x81 -
销毁 Form1
:00404ABC TForm1::~TForm1(this=:02F2AE20)
-
摧毁 Foo
:00404978 Foo::~Foo(this=:00409B74)
:0040493F STDES0()
:0040573f ;IRoot>
:3221910f ; C:\Program Files(x86)\Embarcadero\Studio\18.0\bin\CC32240MT.DLL
:3221915b ; C:\Program Files (x86)\Embarcadero\Studio\18.0\bin\CC32240MT.DLL > :3221944a ; C:\Program Files (x86)\Embarcadero\Studio\18.0\bin\CC32240MT.DLL
【问题讨论】:
-
const String Hello是一个常量对象。它不应该在 main 函数返回之前被销毁。可能它从未被创建,编译器创建了一个字符串常量,而不是作为ShowMessage函数的参数。您可以使用调试器进行检查。 -
您的带有自定义类的示例值得在析构函数中设置断点。查看 Form1 析构函数的调用堆栈。我怀疑 Form1 不是在 hesp 上创建的,而是作为文件范围验证的。在这种情况下,您有一个实现定义的对构造函数和析构函数的调用顺序。这会导致您的程序出现未定义的行为。
-
如果 ShowMessage 是系统函数,那么您必须切换到反汇编视图。使用 ABI 规范如何将参数传递给函数并查看内存转储。所以你可以看到传递给函数的内容。
-
是什么导致这些东西被破坏?它是显式的程序代码,还是作为程序终止的一部分被销毁?在后一种情况下,我不认为 C++ 对销毁静态分配的对象的顺序提供任何保证。
-
@KeremD:
TObject派生类,包括TForm,总是在堆上分配。在您的示例中,String位于全局内存中,并在 RTL 销毁拥有您的TForm1对象的全局TApplication对象之前被编译器释放。 IOW,您的TForm1比您的String寿命更长,这就是为什么它在您的OnDestroy事件处理程序中为NULL(顺便说一句,您永远不应该在C++ 中使用!改用~TForm1析构函数)。
标签: c++ clang c++builder firemonkey object-lifetime