在初始化过程中是否存在动态绑定



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")。我需要解决这类问题。

工作示例代码

这是相当棘手的,因为析构函数在离开作用域时被自动调用,无论是通过正常流程,break, continue, return还是throw。这也是为什么不能向析构函数传递实参的原因。

直接的解决方案是从Derived::~Derived调用Derived::DeInit。这还有一个额外的好处,即仍然有Derived成员可用。

另一个方法是创建自己的智能指针类,在T::~T之前调用T::DeInit。为了防止被绕过,请从Create返回此智能指针。

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

和在发现析构函数时从析构函数中清除虚函数调用

你不需要有一个虚拟的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;
    }

输出:派生的基地

这是你想要的吗?

受salters第二个想法启发的解决方案。

这个解决方案只需要更改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");

顺便说一句:我最终选择重写代码。我认为这是可管理的,结果代码将会更简单(因此更好)。

相关内容

  • 没有找到相关文章

最新更新