C ++ - 销毁顺序 - 在主类析构函数

时间:2017-11-18 17:10:21

标签: c++ static destructor static-members members

我正在使用Embarcadero RAD Studio XE7编译器创建一个c ++项目。在这个项目中,我有以下代码设计:

  • 继承自TForm的主要表单,其中包含析构函数
  • 一个班级" foo"
  • 课程" bar"其中班级" foo"是一个静态成员

现在从主窗体析构函数中我需要执行foo类中包含的函数。所以在我的主要形式析构函数中,我已经放置了这样的代码:

__fastcall TForm1::~TForm1()
{
    Bar::m_Foo.ExecuteSomething();
}

然而,此时我的应用程序崩溃,在我的情况下使用"纯虚函数调用"错误(这个错误当然取决于我的实现,我不会在这里详细介绍)。事实是我的Bar :: m_Foo类在TForm1析构函数之前被删除了。

为了概述这个问题,我在这里重新创建了一个最小的代码示例:

Main.h

#ifndef MainH
#define MainH

#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>

class TForm1 : public TForm
{
    __published:

    public:
        __fastcall TForm1(TComponent* Owner);
        virtual __fastcall ~TForm1();

    private:
};
extern PACKAGE TForm1 *Form1;
#endif

Main.cpp的

#include <vcl.h>
#pragma hdrstop
#include "Main.h"

#include <iostream.h>

#pragma package(smart_init)
#pragma resource "*.dfm"

//---------------------------------------------------------------------------
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    std::cout << "TForm1 constructor - CALLED" << std::endl;
}
//---------------------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
    std::cout << "TForm1 destructor - CALLED" << std::endl;
}
//---------------------------------------------------------------------------

Class.h

#ifndef AClassH
#define AClassH

#include <Windows.h>

class Foo
{
    public:
        Foo()
        {
            std::cout << "Foo constructor - CALLED" << std::endl;
        }

        virtual ~Foo()
        {
            std::cout << "Foo destructor - CALLED" << std::endl;
        }
};

class Bar
{
    private:
        static Foo m_Foo;
};

#endif

Class.cpp

#include "Class.h"

//---------------------------------------------------------------------------
Foo Bar::m_Foo;
//---------------------------------------------------------------------------

执行后,上面的代码显示以下结果:

Foo constructor - CALLED
TForm1 constructor - CALLED
Foo destructor - CALLED
TForm1 destructor - CALLED

这强调静态成员析构函数被称为 BEFORE 主表单析构函数,因此在我的TForm1析构函数中使用Foo类有害。这个结果让我有些困惑,因为我总是相信静态成员变量在应用程序退出的同时变得超出范围,即 AFTER 调用我的主表单析构函数。但似乎事实并非如此。

所以我的问题是:

  • 关于此类静态成员的规则以及何时超出范围?
  • 为什么在主窗体析构函数之前调用我的Foo析构函数?
  • 这个销毁订单是在c ++标准中定义的还是RAD Studio错误?
  • 在我的情况下,使用表单析构函数调用的函数来释放全局GDI +实例。由于我在共享上下文中使用GDI +(主要的exe PLUS 一个dll),主表单调用可能会释放最终锁定。出于这个原因,静态关键字在这里很重要。但我做错了吗?哪种设计可能会更好?

------------------------- EDIT --------------------- ------------

以上是上例中应用程序主入口点的代码,其中创建并删除了TForm1:

#include <vcl.h>
#pragma hdrstop
#include <tchar.h>
//---------------------------------------------------------------------------
USEFORM("Main.cpp", Form1);
//---------------------------------------------------------------------------
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
    try
    {
         Application->Initialize();
         Application->MainFormOnTaskBar = true;
         Application->CreateForm(__classid(TForm1), &Form1);
         Application->Run();
    }
    catch (Exception &exception)
    {
         Application->ShowException(&exception);
    }
    catch (...)
    {
         try
         {
             throw Exception("");
         }
         catch (Exception &exception)
         {
             Application->ShowException(&exception);
         }
    }
    return 0;
}
//---------------------------------------------------------------------------

1 个答案:

答案 0 :(得分:1)

我终于找到了在我的主窗体析构函数之前调用静态成员析构函数的原因。这是因为Embarcadero RAD Studio编译器特有的属性。

实际上,编译器在创建新项目时会生成一些自动代码。在此代码中(并且可以在上面发布的示例中看到),创建主窗体并将其保存在另一个名为Application的对象中,该对象是一个静态全局对象。

以下是在RAD Studio VCL中声明Application对象的方法:

...

{ Global objects }

var
  Application: TApplication;

...

当然,在这些条件下,主窗体对象依赖于其父级的破坏,并且因为它是一个静态对象,所以当应用程序终止时,所有静态对象都会超出范围,它可以被随机销毁。

这也解释了为什么从主窗体析构函数中调用静态成员函数是错误的,在我的例子中,它导致了如此奇怪的访问冲突。