【问题标题】:Is it possible to know the derived instance type during base class destruction in C++?在 C++ 中的基类销毁期间是否可以知道派生实例类型?
【发布时间】:2016-01-12 01:31:26
【问题描述】:

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

【问题讨论】:

  • 我猜你的意思是自动的,也就是编译器会告诉你? “手动”解决方案是添加一些存储最派生类型的数据成员,例如type_info const&.
  • 没有。至此,所有派生部分都消失了。为什么需要这个?
  • 我想知道您是否可以通过将基本析构函数标记为虚拟并覆盖每个派生类中的析构函数来达到相同的目的
  • @Jimmy:析构函数在这方面很特别,因为覆盖 B::~B 仍然会调用 A::~A。回到A::~A,你不知道你从哪里来。

标签: c++ inheritance destructor


【解决方案1】:

这取决于你需要什么。人们当然会在这里质疑为什么,这是一个值得怀疑的愿望。

在编译时不可能随时从基类中自动知道对象的动态类型。如果您想在编译时知道类型,唯一的解决方案是将该信息包含在基类类型本身中,这基本上是 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 之类的操作),例如出于调试原因,您可以在包含类型信息的基类中添加一个成员:

class Base {
protected:
     type_info const* type;

public:
    Base() {
        type = &typeid(this);
    }
};

class Derived : public Base {
public:
     Derived() {
          type = &typeid(this);
     }
};

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

【讨论】:

  • 你忘了在第二个例子中继承Base :)
【解决方案2】:

使用CRTP时可以这样做,像这样:

template <typename TDerived>
class Base {};

class Derived : public Base<Derived> {};

那么Base 始终知道派生类型。

【讨论】:

    【解决方案3】:

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

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

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

    2。使用一种技术作为virtual constructor idiom 的对应技术,在显式销毁之前调用一个函数(通过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([] () { ... })
        {
        }
    };
    

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

    【讨论】:

      猜你喜欢
      • 2011-11-05
      • 2018-10-18
      • 1970-01-01
      • 2011-03-09
      • 1970-01-01
      • 2017-05-10
      • 2023-03-19
      • 2020-11-02
      • 2011-01-21
      相关资源
      最近更新 更多