2009-09-07 2 views
3

Есть в любом случае я могу изменить этот пример кодаИмитация статических конструкторов в C++?

#include <stdlib.h> 
#include <iostream> 

class Base { 
public: 
    Base() { 
     if(!m_initialized) { 
      static_constructor(); 
      m_initialized = true; 
     } 
    } 
protected: 
    virtual void static_constructor() { 
     std::cout << "Base::static_constructor()\n"; 
    } 
private: 
    static bool m_initialized; 
}; 

bool Base::m_initialized = false; 

class Derived : public Base { 
    void static_constructor() { 
     std::cout << "Derived::static_constructor()\n"; 
    } 
}; 

int main(int argc, char** argv) { 
    Derived d; 
    return(EXIT_SUCCESS); 
} 

Так что Derived::static_constructor() будет вызываться вместо Базы? Я хочу инициализировать кучу статических переменных, и наиболее логичным местом для этого является то, что находится в классе.

+0

Только что понял, что я не могу использовать 'm_initialized', потому что в любом случае когда-либо инициализируется только один производный класс ... – mpen

+0

Нужно ли мне в основном копировать код из базы в каждый производный класс? – mpen

+0

Непосредственно; но есть способы получить то, что вы хотите. Нужны ли инициализаторы при запуске или первом экземпляре? – bdonlan

ответ

4

Я принял это решение из решения Martin V Lowis. Основные отличия заключаются в том, что он использует множественное наследование, и CRTP:

template<class T> 
class StaticInitializer : public T 
{ 
    static bool initialized; 
public: 
    StaticInitializer(){ 
    if(!initialized){ 
     T::static_constructor(); 
     initialized=true; 
    } 
    } 
}; 

template<class T> bool StaticInitializer<T>::initialized; 

class Base : public StaticInitializer<Base> 
{ 
public: 
    static void static_constructor() { 
    std::cout << "Base::static_constructor()\n"; 
    } 
}; 
static Base _base; 

class Derived : public Base, public StaticInitializer<Derived> 
{ 
public: 
    static void static_constructor() { 
     std::cout << "Derived::static_constructor()\n"; 
    } 
}; 
static Derived _derived; 

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

+0

Ах! Теперь это лучше. К сожалению, вы должны наследовать от StaticInitializer в производном классе, но я полагаю, это лучшее, что мы можем сделать. Очень круто! Спасибо – mpen

+0

как вы на самом деле компилируете этот код / – lalitm

3

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

#include <iostream> 
using namespace std; 

template<class T> 
class StaticInitializer{ 
    static bool initialized; 
public: 
    StaticInitializer(){ 
    if(!initialized){ 
     T::static_constructor(); 
     initialized=true; 
    } 
    } 
}; 

template<class T> bool StaticInitializer<T>::initialized; 

class Base{ 
public: 
    static void static_constructor() { 
    std::cout << "Base::static_constructor()\n"; 
    } 
}; 
static StaticInitializer<Base> _base; 

class Derived{ 
public: 
    static void static_constructor() { 
     std::cout << "Derived::static_constructor()\n"; 
    } 
}; 
static StaticInitializer<Derived> _derived; 

int main() 
{} 
+0

Как хорошо, как это решение ... тогда базовые и производные имеют тип 'StaticInitializer', и я не могу использовать их другие функции-члены ... – mpen

5

Вы никогда не должны вызывать виртуальные функции от конструктора (или деструктора)! Результат не будет «ожидаемым» (следовательно, результат, который вы видите). Зачем? Потому что базовый конструктор (Base) вызывается перед конструктором Derived. Это означает, что локальные данные в Derived, на которые может ссылаться виртуальная функция, еще не инициализированы. Кроме того, возможно, что еще более важно, vtable еще не был инициализирован функциями в Derived, но только с элементами из Base. Следовательно, виртуальная функция еще не виртуальна - она ​​не будет до тех пор, пока Base() не завершится и не будет обработана Derived().

Кроме того, это приведет к поломке Open/Closed-principle, который, вкратце, гласит: «классы должны быть открыты для расширения, но закрыты для модификации». Вы изменяете базовую статическую инициализацию, пытаясь изменить ее поведение, а не расширять его. Это может показаться хорошей идеей, в то время, но, скорее всего, он будет кусать свою задницу позже;)

+0

Ahh .. это имеет смысл. Это не отвечает на мой вопрос, но это очень полезное объяснение. +1 – mpen

+0

+1 для объяснения vtable. – RichieHindle

0

Я предлагаю это решение:

#include <cstdlib> 
#include <iostream> 

class Object { 
public: 
    Object() { 
     std::cout << m_var << std::endl; 
    } 
private: 
    static int m_var, init_var(); 
}; 

int Object::init_var() { 
    return 5; 
} 

int Object::m_var = Object::init_var(); 

int main(int argc, char** argv) { 
    Object o; 
    return(EXIT_SUCCESS); 
} 

Этот способ m_var только инициализируется один раз, и я продолжаю весь код конструкции внутри класса.

Смежные вопросы