[英]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
}
...
并在发现析构函数时从析构函数中清除虚函数调用。
这很棘手,因为析构函数会在离开范围时自动调用,无论是通过正常流程, break
, continue
, return
还是throw
。 这就是为什么您不能将参数传递给析构函数的原因。
直接的解决方案是从Derived::~Derived
Derived::DeInit
调用Derived::DeInit
。 这还有使Derived
成员可用的额外好处。
另一个方法是创建自己的智能指针类, T::DeInit
在T::~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.