Так как инициализация C ++ в разных TU - это большая серая область с большим количеством возможностей для реализации, я предпочитаю полностью отказаться от нее и четко указать, что будет сделано, когда. (Это отклонение порядка инициализации из-за отсутствия гарантий аналогично тому, как одноэлементные классы отклоняют глобальные объекты.) В частности, это означает любое глобальное состояние (глобальные переменные, члены-статические данные и статические функции), которые не может быть инициализирован с помощью константных выражений, должен быть инициализирован ровно в одном TU, и этот TU является тем, который реализует main .
В ручном случае это означает вставку и обновление кода в блоке перевода, который содержит main и в main . Наиболее распространенным примером такого кода является вызов 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 на основе соглашений об именах файлов, используя защитные элементы include для включения зависимостей (так же, как включаемые охранники работают для заголовков).
Например, оба этих файла соответствуют 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, если вы хотите узнать больше.