簡體   English   中英

在反初始化過程中是否有任何動態綁定

[英]Is there any Dynamic Binding During Deinitialization idiom

Aka:是否有“在初始化期間調用虛擬機”的成語

我正在清理一些舊代碼,需要修復在構造函數和析構函數中調用虛擬方法的情況。 我不知道代碼庫,它很大。 不能進行重大重寫。

構造函數的修復很簡單。 我將虛擬調用移至靜態的Create模板,並保護了所有構造函數。 然后,我要做的就是編譯並更改所有位置,從而導致使用Create模板時出錯。 回歸的機會最小。 但是,對於析構函數,沒有類似的方法。

您將如何解決?

范例程式碼

#include <iostream>

class Base
{
public:
    virtual ~Base()
    {
        DeInit();
    }
protected:
    virtual void DeInit()
    {
        std::cout << "Base" << std::endl;
    }
};

class Derived : public Base
{
protected:
    virtual void DeInit() override
    {
        std::cout << "Derived" << std::endl;
        Base::DeInit();
    }
};

int main()
{
    Derived d;
}

此代碼不調用Derived::DeInit (僅打印“ Base”)。 我需要解決此類問題。

工作示例代碼

...
virtual Base::~Base()
{
    Base::DeInit();
}
...

...
Derived::~Derived()
{
    // de-initialization code
    // do not call Derived::DeInit() here as otherwise Base::DeInit()
    // will be called two times
}
...

並在發現析構函數時從析構函數中清除虛函數調用。

這很棘手,因為析構函數會在離開范圍時自動調用,無論是通過正常流程, breakcontinuereturn還是throw 這就是為什么您不能將參數傳遞給析構函數的原因。

直接的解決方案是從Derived::~Derived Derived::DeInit調用Derived::DeInit 這還有使Derived成員可用的額外好處。

另一個方法是創建自己的智能指針類, T::DeInitT::~T T::DeInit之前調用T::DeInit 為避免被繞過,請從Create返回此智能指針。

您不需要虛擬的DeInit樂趣。

    #include <iostream>

    class Base
    {
    public:
        virtual ~Base()
        {
            DeInit(); //this calls Base version
        }
    protected:
        void DeInit()
        {
            std::cout << "Base" << std::endl;
        }
    };

    class Derived : public Base
    {

    public:
         ~Derived()
        {
            DeInit(); //this calls Derived version
        }
    protected:
        void DeInit() 
        {
        std::cout << "Derived" << std::endl;
        }
    };

    int main()
    {
        Derived d;
    }

輸出:衍生基數

這就是你想要的嗎?

受MSalters第二個想法啟發的解決方案。

此解決方案僅需要更改Base類和Derived類的實例。 無需對任何Derived實施進行任何更改。

#include <iostream>
#include <memory>

class Base
{
private:
    template <class T>
    class WithAutoDeInit : public T
    {
    public:
        virtual ~WithAutoDeInit() override
        {
            T::DeInit();
        }
    };

public:
    template <class T>
    static std::unique_ptr<typename std::enable_if<std::is_base_of<Base, T>::value, WithAutoDeInit<T>>::type> Create()
    {
        return std::make_unique<WithAutoDeInit<T>>();
    }

    virtual ~Base() = default;

protected:
    virtual void DeInit()
    {
        std::cout << "Base" << std::endl;
    }
};

class Derived : public Base
{
protected:
    virtual void DeInit() override
    {
        std::cout << "Derived" << std::endl;
        Base::DeInit();
    }
};

int main()
{
    Base::Create<Derived>();
}

工作示例代碼

這不是一個可靠的解決方案。 您仍然可以直接制作“ Derived實例。 而且,如果您使用受保護的構造函數更新所有Derived類,那么一個不知情的開發人員仍然會創建一個新類,而忘記使其構造函數受到保護。 我想知道這是否可以通過戰略位置中的某種主張來實施?

static_assert(std::is_constructible<Derived>::value, "Derived class is constructable");

順便說一句:我終於選擇重新編寫代碼。 我認為這是可管理的,並且生成的代碼會更簡單(因此更好)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM