简体   繁体   English

在反初始化过程中是否有任何动态绑定

[英]Is there any Dynamic Binding During Deinitialization idiom

Aka: Is there any "Calling Virtuals During Deinitialization" idiom Aka:是否有“在初始化期间调用虚拟机”的成语

I am cleaning up some old code and need to fix cases where virtual methods are called in constructors and destructors. 我正在清理一些旧代码,需要修复在构造函数和析构函数中调用虚拟方法的情况。 I don't know the code base and it is huge. 我不知道代码库,它很大。 Major rewrite is not an option. 不能进行重大重写。

The fix for constructors was simple. 构造函数的修复很简单。 I moved the virtual calls to a static Create template and made all the constructors protected. 我将虚拟调用移至静态的Create模板,并保护了所有构造函数。 Then all I needed to do was to compile and change all location causing errors to use the Create template. 然后,我要做的就是编译并更改所有位置,从而导致使用Create模板时出错。 Minimal chance for regressions. 回归的机会最小。 However there is no analog to this for destructors. 但是,对于析构函数,没有类似的方法。

How would you solve this? 您将如何解决?

Example code 范例程式码

#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;
}

This code does not call Derived::DeInit (only prints "Base"). 此代码不调用Derived::DeInit (仅打印“ Base”)。 I need to fix this kind of issues. 我需要解决此类问题。

Working example code 工作示例代码

...
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
}
...

and cleanup of virtual function calls from destructors when spotting them. 并在发现析构函数时从析构函数中清除虚函数调用。

This is quite tricky, as destructors are called automatically on leaving scopes, whether by normal flow, break , continue , return or throw . 这很棘手,因为析构函数会在离开范围时自动调用,无论是通过正常流程, breakcontinuereturn还是throw That's also why you can't pass arguments to a destructor. 这就是为什么您不能将参数传递给析构函数的原因。

The straightforward solution is to call Derived::DeInit from Derived::~Derived . 直接的解决方案是从Derived::~Derived Derived::DeInit调用Derived::DeInit This has the additional benefit of still having Derived members available. 这还有使Derived成员可用的额外好处。

Another is to create your own smart pointer class, which calls T::DeInit before T::~T . 另一个方法是创建自己的智能指针类, T::DeInitT::~T T::DeInit之前调用T::DeInit To prevent this from being bypassed, return this smart pointer from your Create . 为避免被绕过,请从Create返回此智能指针。

you dont need to have a virtual DeInit fun. 您不需要虚拟的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;
    }

Output: Derived Base 输出:衍生基数

is this what you wanted? 这就是你想要的吗?

A solution inspired by MSalters second idea. 受MSalters第二个想法启发的解决方案。

This solution only requires changes to Base class and to instantiation of Derived classes. 此解决方案仅需要更改Base类和Derived类的实例。 No changes needed to any Derived implementation. 无需对任何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>();
}

Working example code 工作示例代码

This is not a robust solution. 这不是一个可靠的解决方案。 You can still make instances of Derived directly. 您仍然可以直接制作“ Derived实例。 And if you update all your Derived classes with protected constructors an unknowing developer could still create a new class forgetting to make its constructors protected. 而且,如果您使用受保护的构造函数更新所有Derived类,那么一个不知情的开发人员仍然会创建一个新类,而忘记使其构造函数受到保护。 I wonder if this could be enforced by some kind of assert in a strategic location? 我想知道这是否可以通过战略位置中的某种主张来实施?

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

Btw: I finally choose to rewite the code. 顺便说一句:我终于选择重新编写代码。 It is managable I think, and the resulting code will off cause be simpler (hence better). 我认为这是可管理的,并且生成的代码会更简单(因此更好)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 需要有关初始化过程中动态绑定的帮助 - Need help on the Dynamic Binding During Initialization idiom C++ Mixin - 初始化过程中的动态绑定习语 - C++ Mixin - Dynamic Binding During Initialization idiom 在静态取消初始化期间是否可以保证cout? - Is cout guaranteed available during static deinitialization? C++ Nifty Counter 成语如何保证 static 初始化和 static 去初始化 - How does C++ Nifty Counter idiom guarantee both static initialization and static deinitialization 静态取消初始化期间,静态分配的内存会变得无效吗? - Can static allocated memory become invalid during static deinitialization? OpenCL-C ++包装器-动态库中的上下文取消初始化会导致访问冲突 - OpenCL - C++ wrapper - Context deinitialization in dynamic library leads to access violation 不使用动态内存分配的 Pimpl 习惯用法 - Pimpl idiom without using dynamic memory allocation C ++中的“动态模板”习惯用法,是否有标准名称? - “dynamic template” idiom in C++, is there a standard name for this? 带有模板类的pimpl习语是否有任何优势? - Is there any advantage to the pimpl idiom with a templated class? 有没有什么理由说StringPiece / StringRef成语不是更受欢迎? - Are there any reasons why the StringPiece/StringRef idiom is not more popular?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM