在C ++中基类销毁期间是否可以知道派生的实例类型?

时间:2015-10-14 05:26:53

标签: c++ inheritance destructor

说我有 private void functionMap() { "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta http-equiv=\"Content-Type\" content=\"text/html,width=device-width,user-scalable=yes; charset=windows-1252\">\n" + " <script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"></script>\n" + " <script type=\"text/javascript\">\n" + " google.load('visualization', '1', {'packages': ['geochart']});\n" + " google.setOnLoadCallback(drawRegionsMap);\n" + "\n" + " function drawRegionsMap() {\n" + " var data = google.visualization.arrayToDataTable([\n" + " ['Country', ''],\n" " ]);\n" + "\n" + " var options = {};\n" + "\t\toptions = { \n" + " datalessRegionColor: '#29ABE2',\n" + "\t\t\t\t backgroundColor: '#F3F3F3',\n" + "\t\t\t\t\tcolorAxis: {colors: ['#0071BC']},\n" + " keepAspectRatio: false, \n" + " legend: false,\n" + " tooltip: { textStyle: { color: '#0099CB', fontName: 'Arial', fontSize: '10'} }\n" + " };\n" + "\t\t\n" + " var chart = new google.visualization.GeoChart(document.getElementById('chart_div'));\n" + " chart.draw(data, options);\n" + " };\n" + " </script>\n" + " </head>\n" + " <body>\n" + " <div id=\"chart_div\" style=\"width: 100%; height: 100%;\"><div style=\"position: relative;\"><div dir=\"ltr\" style=\"position: relative; width: 100%; height: 100%;\"><div style=\"position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;\"></div></div></div></div>\n" + " \n" + "</body></html>"; } 的纯课程A和课程BCD等。是否有可能从A的析构函数中知道哪个派生类正在被销毁?

3 个答案:

答案 0 :(得分:4)

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

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

class BaseBase {
    // body
};

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

class Derived1 : public Base<Derived1> {
    // body
};

class Derived2 : public Base<Derived2> {
    // body
};

这样Base的析构函数将在编译时#34;知道&#34;编译时派生类的类型。然而,这有一个缺点,即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指针将引用当前已构造的最派生类。

答案 1 :(得分:3)

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

template <typename TDerived>
class Base {};

class Derived : public Base<Derived> {};

然后Base始终知道派生类型。

答案 2 :(得分:0)

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([] () { ... })
    {
    }
};

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