在c++中基类销毁期间是否有可能知道派生实例类型?

Is it possible to know the derived instance type during base class destruction in C++?

本文关键字:有可能 派生 实例 类型 是否 c++ 基类      更新时间:2023-10-16

假设我有纯类A和从A派生的类BCD等。是否有可能从A的析构函数中知道哪个派生类正在被销毁?

那要看你需要什么了。当然有人会问为什么,这是一个值得怀疑的愿望。

不可能在编译时随时从基类中自动知道对象的动态类型。如果您想知道编译时的类型,唯一的解决方案是在基类类型本身中包含该信息,这基本上是CRTP模式。例如:

class BaseBase {
    // body
};
template <typename D>
class Base : BaseBase {
    //body
};
class Derived1 : public Base<Derived1> {
    // body
};
class Derived2 : public Base<Derived2> {
    // body
};

这样,Base的析构函数将在编译时"知道"派生类的类型。然而,这有一个缺点,Derived1Derived2的共同超类型是而不是 Base,但是BaseBaseBaseBase的析构函数无法知道(并且您可能会回到原点)。

如果你只想在运行时知道(这意味着你不能直接做像DerivedClass::something这样的事情),例如出于调试原因,你可以在基类中添加一个包含info类型的成员:

class Base {
protected:
     type_info const* type;
public:
    Base() {
        type = &typeid(this);
    }
};
class Derived : public Base {
public:
     Derived() {
          type = &typeid(this);
     }
};

请注意,这依赖于Base构造函数将在Derived构造函数之前运行的事实,因此type指针将指向当前已构造的最派生类。

可以在使用CRTP时完成,如下所示:

template <typename TDerived>
class Base {};
class Derived : public Base<Derived> {};

那么Base在任何时候都知道派生类型

CRTP的问题是您有不同的基类型,您不能将其用于动态多态性。

如果您不需要派生对象的实际类型,但希望根据它执行代码,则有不同的可能性。注意,不能在析构函数中调用任何虚函数。但是,您可以:

1。添加一个类型成员变量(可以是std::type_info,枚举或其他)并在其上手动调度。

2。使用一种技术作为虚拟构造函数习惯用法的对应,在显式销毁之前调用函数(通过delete或类似的方法)。然而,这要么可以被遗忘,要么(如果强制执行)严重限制了销毁对象的方式。

3。您可以使用策略模式:

class Base
{
    struct Strategy
    { 
        virtual ~Strategy();
        virtual void onDestroy() = 0;
    }
    std::unique_ptr<Strategy> strategy;
public:
    explicit Base(std::unique_ptr<Strategy> strategy)
    : strategy(std::move(strategy))
    {
    }
    virtual ~Base()
    {
        strategy->onDestroy();
    }
};
class Derived1 : public Base
{
    struct Strategy1 : Strategy
    {
        virtual void onDestroy() { ... }
    };
public:
    Derived1()
    : Base(std::make_unique<Strategy1>())
    {
    }
};

注意,对于c++ 11函数对象,这变得相当简单:

class Base
{
    std::function<void()> strategy;
public:
    explicit Base(std::function<void()> strategy)
    : strategy(std::move(strategy))
    {
    }
    virtual ~Base()
    {
        strategy();
    }
};
class Derived1 : public Base
{
public:
    Derived1()
    : Base([] () { ... })
    {
    }
};

重要:您必须确保策略对象或函数对象不引用派生类的任何成员,因为派生对象在调用时已被销毁。如果需要访问成员属性,最好直接重新定义析构函数。

相关文章: