2015-10-14 3 views
3

Скажем, у меня чистый класс A и класс B, C, D и т. Д., Исходя из A. Можно ли узнать из деструктора A, который разрушает производный класс?Возможно ли узнать тип производного экземпляра при уничтожении базового класса в C++?

+1

Я предполагаю, что вы имеете в виду автоматически, то есть компилятор должен сказать вам? «Ручное» решение состоит в том, чтобы добавить некоторый элемент данных, который хранит наиболее производный тип, например. 'type_info const &'. – dyp

+2

Нет. К этому моменту все производные части исчезли. Зачем тебе это нужно? –

+0

Я хотел бы знать, можно ли достичь тех же целей, обозначив базовый деструктор как виртуальный и переопределив деструктор в каждом из производных классов. – Jimmy

ответ

3

Может быть сделано, когда CRTP используется, например:

template <typename TDerived> 
class Base {}; 

class Derived : public Base<Derived> {}; 

Тогда Base знает производный тип в любое время.

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 будет во время компиляции «знать» тип производного класса во время компиляции. Однако это имеет тот недостаток, что общий супер типа Derived1 и Derived2 является неBase, но BaseBase и BaseBase деструктора не может знать (и вы можете быть обратно на площади один).

Если вы хотите знать только во время выполнения (что означает, что вы не можете напрямую делать такие вещи, как 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 будет ссылаться на самый производный класс, который в настоящее время был построен.

+0

Вы забыли наследовать 'Base' во втором примере :) –

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

Важно: Вы должны убедиться, что объект стратегии или объект функции не ссылается ни на какие члены производного класса, поскольку производный объект был уничтожен во время вызова. Если вам нужно получить доступ к свойствам членов, вам будет лучше переопределить деструктор напрямую.