Class destructor

Top  Previous  Next

What is translated > Types > Records, Classes, Interfaces > Class > Destructors > Class destructor

In Delphi, besides instance destructors, there is a class destructor. It runs once per type at program (or module) shutdown, mirroring the unit finalization phase. Execution order is reverse of initialization: the derived type’s class destructor runs before the base type’s class destructor.

 

A Delphi class destructor is declared as:

 

class destructor Destroy;

 

C++ has no direct equivalent to Delphi’s class destructor, and adding a second “static destructor” with the same name as the instance destructor would be ambiguous. Delphi2Cpp therefore emits a one-time finalizer and registers it via std::atexit. The registration is performed during the type’s one-time initialization (EnsureClassInitialized). If the Delphi class defines a class destructor but no class constructor, Delphi2Cpp synthesizes a minimal ClassCreate that only calls RegisterAtExit() (guarded by std::call_once) so the finalizer is still registered.

 

 

class MyType

{

public:

    static void EnsureClassInitialized();   // call at public entry points

 

protected:

    static void ClassCreate();              // Delphi: class constructor (emulated)

    static void ClassDestroy();             // Delphi: class destructor (emulated)

 

private:

    static void RegisterAtExit();

 

    static std::once_flag m_initFlag;

    static std::once_flag m_atexitFlag;

};

 

// MyType.cpp

#include "MyType.hpp"

 

std::once_flag MyType::m_initFlag;

std::once_flag MyType::m_atexitFlag;

 

void MyType::EnsureClassInitialized()

{

    std::call_once(m_initFlag, []()

    {

        ClassCreate();

        RegisterAtExit();   // register teardown exactly once

    });

}

 

void MyType::RegisterAtExit()

{

    std::call_once(m_atexitFlag, []()

    {

        std::atexit([]()

        {

            // Never throw from atexit; swallow/log instead.

            try { ClassDestroy(); } catch (...) {}

        });

    });

}

 

void MyType::ClassDestroy()

{

    // One-time teardown; mirrors Delphi "class destructor"

    // (idempotent; do not throw)

    ...

}

 

 

std::call_once guarantees the passed callable runs exactly once across all threads for the given flag. If the callable throws, the “once” state is not set, so a later call retries. A successful run establishes a happens-before relation for subsequent callers.
std::atexit order is LIFO (last registered, first called). If the derived type calls EnsureClassInitialized() (which first initializes the base, then itself) and then registers its own ClassDestroy, the LIFO order naturally produces derived-first, base-after destruction—matching Delphi.
Make ClassDestroy() idempotent and no-throw. During process teardown other statics may already be gone; guard against re-entry and avoid exceptions.

 

 



This page belongs to the Delphi2Cpp Documentation

Delphi2Cpp home  Content