2010-02-25 2 views
1

Язык: C++ Toolkit: Qt4Можно ли использовать статическую переменную для инициализации/регистрации переменных?

Инструментарий Я использую имеет статический метод, называемый int QEvent::registerEventType() зарегистрировать свои собственные типы событий. Когда я подклассифицирую это QEvent, мне нужно предоставить базовый класс это значение. QEvent::QEvent(int type).

Можно ли использовать статическую переменную для вызова этого приложения до запуска приложения? Рассмотрим следующее:

//This is all in my .cpp file 

static int myEventType; //This will contain my registered type 

/*If I create a static instance of this class the constructor 
    gets called before the main() function starts. 
*/ 
class DoRegisterMyEventType { 
public: 
    DoRegisterMyEventType() { 
    myEventType = QEvent::registerEventType(); 
    } 
}; 

static DoRegisterMyEventType doRegisterMyEventType; 

//Here is the constructor for MyEvent class which inherits QEvent. 
MyEvent::MyEvent() 
    : QEvent(myEventType) 
{ 
} 

Как это «зло»? Я мог бы обернуть все это в пространство имен, чтобы предотвратить загрязнение глобального пространства имен.

+0

Думая об этом, я понимаю, что я мог бы, вероятно, просто назвать QEvent :: registerEventType() в конструкторе моего объекта MyEvent следующим образом: MyEvent :: MyEvent(): QEvent (QEvent :: registerEventType()) {}, а затем предоставит метод, возвращающий этот тип или что-то в этом роде. – darkadept

+0

Я считаю, что работает только в том случае, если MyEvent сам по себе является одноэлементным классом, что не очевидно из того, что вы уже сказали. Точкой хранения идентификатора события (int) отдельно является то, что вы можете надежно передавать одно и то же значение в QEvent ctor из вашего MyEvent ctor каждый раз. – 2010-02-26 04:47:41

ответ

1

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

static inline int GetMyEventType() 
{ 
    static int sEventType = QEvent::registerEventType(); 
    return sEventType; 
} 

MyEvent::MyEvent() 
    : QEvent(GetMyEventType()) 
{ 
} 

Это решение обладает свойством registerEventType гарантирована назвать прежде чем вам нужно ввести ваше мероприятие, даже если вы строите MyEvent во время статической инициализации, которая хороша, но она откроет вам до потокобезопасности вопросов, если возможно, MyEvent будет построен на нескольких потоках.

Вот поточно-версия, основанная на буст :: call_once:

#include "boost/thread/once.hpp" 

static boost::once_flag sHaveRegistered = BOOST_ONCE_INIT; //This is initialized statically, effectively at compile time.  
static int sEventType = -1; //-1 is not a valid event 

static void DoRegister() 
{ 
    sEventType = QEvent::registerEventType(); 
} 

static inline int GetMyEventType() 
{ 
    boost::call_once(sHaveRegistered, &DoRegister); 
    return sEventType; 
} 
+0

Обратите внимание, что это фактически тот же самый метод инициализации, который используется «одиночным тоном Майера» в ответе Нила, но лучше реорганизует проблемы. – 2010-02-26 00:31:58

+0

Я полностью согласен, что это одна и та же конструкция, но я также думаю, что это немного яснее ответа Нила. Кажется немного сложнее иметь полное определение класса для хранения целого числа. Также довольно сложно (хотя и не невозможно) сделать версию класса безвредной (что может быть или не быть проблемой для OP, но это, как правило, вызывает беспокойство в моей жизни). –

+0

На самом деле главное отличие заключается в том, что ответ Нила будет реорганизовать сам реестр (QEvent), а не использовать его, и я немного предпочитаю ваш в этом смысле (и попытался сказать это в своем комментарии). Однако, когда вы говорите об одном int и подобных случаях, я предпочитаю свой ответ (конечно: P), который сводится к одной строке «int variable = some_func()»; * выполняется в нужное время * (рассмотрите, например, как мой ответ отлично потокобезопасен). Механизм, который это делает, становится сложным (но только требует установки один раз), и этот механизм составляет основную часть моего ответа. – 2010-02-26 02:42:05

1

Я использую шаблон «статический регистр объекта» совсем немного, но вы должны знать о одной большой проблеме - вы должны убедиться, что вещь, с которой вы регистрируетесь, которая сама по себе статична, создается до вещь, которую вы регистрируете. Поскольку C++ не гарантирует порядок статической конструкции между единицами перевода, это может быть проблематично. Одним из решений является использование так называемого Meyer Singleton:

class Registry { 
    public: 
    static Registry & Instance() { 
     static Registry r; 
     return r; 
    } 

    ... 

private: 
    Registry() {  
     ... 
    } 
}; 

Как все ссылки на реестр должен пройти через метод Instance(), вы гарантированно требуемый порядок строительства.

+0

В то время как порядочный совет, я не думаю, что OP имеет широту, чтобы изменить QEvent таким образом. – 2010-02-25 20:57:15

+0

@Roger Я думаю - я сам не использую Qt. – 2010-02-25 21:02:53

+0

Это правильно. Я не могу изменить инструментарий. Есть ли способ обеспечить статический порядок инициализации? – darkadept

2

Поскольку инициализация C++ через TU - это большая серая область с большим объемом реализации, я предпочитаю полностью ее обрывать и четко указывать, что делается, когда. (Это отклонение порядка инициализации из-за отсутствия гарантий аналогично тому, как singleton classes отклоняет глобальные объекты.) В частности, это означает любое глобальное состояние (глобальные переменные, статические члены данных и функция-локальная статика), которые не могут быть инициализированы постоянными выражениями должен быть инициализирован точно в одном ТУ, и что ТУ является тем, который реализует основной.

В ручном случае, это означает, что вставки и обновления кода в ЕП, который содержит главный и в самой основной. Наиболее распространенным примером такого кода является вызов srand(time(0)) для посева std :: rand PRNG.

Вы можете реорганизовать, что ручное управление кодом с помощью препроцессора:

// the implementation file for main, could be named main.cpp 

#include "whatever_declares_the_real_main.hpp" 

#include "global_objects.inc" 

int main(int argc, char* argv[]) try { 
#include "main_init.inc" 

    return the_real_main(argc, argv); 

    // main.cpp has well-defined responsibility: 
    // initialize global state before passing control to another function, and 
    // handle return-code or exceptions 

    // you can modify this, depending on your preference and desired API 
    // for example: 
    return the_real_main(std::vector<std::string>(argv+1, argv+argc)); 
    return the_real_main(parse_args(argv+1, argv+argc)); 
    // just make sure to keep main.cpp's responsibility well-defined and 
    // relatively simple 
} 
// example handling; depending on your specifics, you might do something 
// different, or know how to provide more information: 
catch (std::exception& e) { 
    std::cerr << "abnormal termination: " << e.what() << '\n'; 
    return 1; 
} 
catch (...) { 
    std::cerr << "abnormal termination.\n"; 
    return 1; 
} 

Эти .INC файлы не являются ни заголовки, ни файлы реализации. Точное расширение файла не имеет значения, если вы не используете что-то, что обычно используется для заголовков или файлов реализации, таких как .h, .hpp, .cc, .cpp и т. Д. Вы можете генерировать global_objects.inc и main_init.inc основываются на соглашениях об именах файлов, используя включенные охранники, чтобы можно было включить зависимости (так же, как включение охранников для заголовков).

Например, оба этих файла соответствуют myevent.HPP и будет размещен рядом с этим заголовком:

// file "myevent.global_inc" 
#ifndef INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868 
#define INCLUDE_GUARD_37E6F5857F8F47918A7C83F29A9DA868 

#include <QEvent.hpp> // or whatever headers you need 

#include "myevent.hpp" // declares the variable defined just below 
// (remember you use 'extern' to declare objects without defining them) 

int your_namespace::myEventType = QEvent::registerEventType(); 

#endif 

// file "myevent.main_inc" 
#ifndef INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81 
#define INCLUDE_GUARD_4F1B93D0F4D3402B802CBA433241AA81 

// nothing needed in this case, from what you've shown so far 

// this is where you place expressions that would otherwise require a dummy 
// global variable to make sure they are executed, but this also allows use 
// of temporary variables while includes handle dependency order: 
#include "something_else.main_inc" // fake example dependency, which must 
{         // be executed first 
    int temp; 
    some_func(&temp); 
    other_func(temp); // not easy to transform this into a global's init 
    // expression, yet defining it this way is natural, because it's exactly 
    // how you would do it inside a function 
} 

#endif 

Обратите внимание, что если требуется только статическая инициализация данных с постоянными выражениями, то, что предпочтительнее, чем все другие методами. Основное ограничение для этой инициализации не позволяет выполнить вызов функции (но это на самом деле более сложный), поэтому он не применяется в вашем случае; это единственный тип инициализации глобальной переменной, который может выполнять C, если вы хотите узнать больше.

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