语言:C ++ 工具包: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 :(得分:2)
由于C ++的初始化跨越TU 是一个很大的灰色区域,具有很大的实现余地,我更喜欢完全废弃它并明确说明什么时候完成。 (由于缺少保证而拒绝初始化顺序与singleton classes拒绝全局对象的方式相似。)具体来说,这意味着任何无法初始化的全局状态(全局变量,静态数据成员和函数局部静态)使用常量表达式必须在一个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 ,以便可以包含依赖项(就像包含保护工作的标题一样)。
例如,这两个文件都与 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可以做的唯一一种全局变量初始化。
答案 1 :(得分:1)
我使用“静态寄存器对象”模式相当多,但你必须意识到一个大问题 - 你必须确保你注册的东西,它本身可能是静态的,是在事物之前创建的你正在注册。由于C ++不保证翻译单元之间静态构造的顺序,这可能是有问题的。一种解决方案是使用所谓的Meyer Singleton:
class Registry {
public:
static Registry & Instance() {
static Registry r;
return r;
}
...
private:
Registry() {
...
}
};
由于对注册表的所有引用都必须通过Instance()方法,因此可以保证所需的构造顺序。
答案 2 :(得分:1)
静态级别初始化是一个巨大的编译器相关灰色区域,正如其他人提到的那样。但是,功能级别初始化不是灰色区域,可以用于您的优势。
static inline int GetMyEventType()
{
static int sEventType = QEvent::registerEventType();
return sEventType;
}
MyEvent::MyEvent()
: QEvent(GetMyEventType())
{
}
这个解决方案具有registerEventType保证在你需要你的事件类型之前被调用的属性,即使你在静态初始化期间构造了MyEvent,这很好,但它确实可以解决线程安全问题,如果它可以用于MyEvent在多个线程上构建。
这是一个基于boost :: 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;
}