bentley开发C++\CLI

C++/CLI中的确定性垃圾回收与mdl.net中Element

2022-04-09  本文已影响0人  左图右码

C++/CLI中的确定性垃圾回收

托管语言中自动垃圾回收导致回收的时机是不确定的,只有托管C++实现了确定性的回收机制,在一本名著中这么写到(我翻译过来的):

析构器和终结器的IL实现
在我们对垃圾收集的介绍中,您看到了GC如何在清理对象之前自动调用终结器。很明显。NET直接支持终结器。但没有提到析构器。这是因为NET 框架的自动垃圾收集内存模型不利于确定性析构器的概念。在这种背景下,C++/CLI使用Dispose模式来实现析构器和终结器语义。
C++/CLI Dispose模式使用以下规则:
■如果一个class有终结器(!typename)或析构器(~typename),编译器为该类生成Dispose(bool)方法。编译器生成的代码检查bool,如果传递了true值,它会调用析构器(~typename),如果传递了false值,它会调用终结器(!typename)。
■对于任何定义析构器(~typename)的对象,编译器自动实现该对象的System::IDisposable接口,并生成满足该接口的Dispose()方法。此编译器生成的Dispose方法调用Dispose(true),以便执行析构器(~typename)。然后,它调用GC::SuppressFinalize(this),它将该对象从Finalization队列中删除,因为该对象现在其析构器已被调用,因此无需再使用终结器。
■对于任何定义终结器的对象(!Typename),编译器自动生成一个Finalize方法,该方法包含了System::Object::Finalize()。此编译器生成的Finalize方法调用Dispose(false),以便终结器(!Typename)被执行。

namespace test
{
    public ref class A
    {
    public:
        A() {}
    private:
        !A() {}
    };
    public ref class B
    {
    public:
        B() {}
    private:
        ~B() {}
    };
    public ref class C
    {
    public:
        C() {}
    private:
        !C() {}
        ~C() {}
    };
}

以上代码编译后的IL反编译如下,可以看到编译器塞入了哪些内容,符合上述的解释:

QQ20220409-142325.png

请注意,析构器和终结器都可以放入私有部分,因为delete和GC不是直接访问他们,这和标准C++不同。
另外,如果想要手工执行回收,不需要调用终结器,则GC::SuppressFinalize(Object^)设置就能删除终结器的自动调用。而System::GC::ReRegisterForFinalize(Object^)则会将object的句柄重新加入Finalization终结器列表,就又可以自动回收了。
比如,在mdl.net中最常用的Element中AdjustFinalizeRequirement用于调整是否需要调用终结器(反编译代码):

internal unsafe void AdjustFinalizeRequirement()
{
    int num;
    GC.SuppressFinalize(this);
    ref byte modopt(IsExplicitlyDereferenced) pinned numRef = (ref byte modopt(IsExplicitlyDereferenced)) &(this.ElementHandle[0]);
    EditElementHandle* handlePtr = numRef;
    if ((0L == Bentley.DgnPlatform.ElementHandle.PeekElementDescrCP((ElementHandle modopt(IsConst)* modopt(IsConst) modopt(IsConst)) handlePtr)) && 
(0L == Bentley.DgnPlatform.EditElementHandle.GetIElementState(handlePtr)))
    {
        num = 0;
    }
    else
    {
        num = 1;
    }
    if (((byte) num) != 0)
    {
        GC.ReRegisterForFinalize(this);
    }
}

如果ElementHandle中没有元素描述符和IElementState也为空的话,就没必要调用终结器。

上一篇下一篇

猜你喜欢

热点阅读