English 中文(简体)
Is it ok to use a static variable to initialize/register variables?
原标题:

Language: C++ Toolkit: Qt4

The toolkit I m using has a static method called int QEvent::registerEventType() to register my own event types. When I subclass this QEvent I need to supply the base class this value. QEvent::QEvent(int type).

Is it ok to use a static variable to call this before application starts? Consider the following:

//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)
{
}

How evil is this? I could wrap the whole thing in a namespace to prevent polluting the global namespace.

最佳回答

Static level initialization is a huge compiler-dependent grey area, as others have mentioned. However, function level initialization is not a grey area and can be used to your advantage.

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

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

This solution has the property that registerEventType is guaranteed to be called before you need your event type even if you construct MyEvent during static initialization, which is good, but it does open you up to thread-safety issues if it s possible for MyEvent to be constructed on multiple threads.

Here s a thread-safe version, based on 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;
}
问题回答

Since C++ s initialization across TUs is a big grey area with much implementation leeway, I prefer to scrap it completely and be explicit about what gets done when. (This rejection of initialization order due to lack of guarantees is similar to how singleton classes reject global objects.) Specifically, this means any global state (global variables, static data members, and function-local statics) that cannot be initialized with constant-expressions must be initialized in exactly one TU, and that TU is the one that implements main.

In the manual case, this means inserting and updating code in the translation unit that contains main and in main itself. The most common example of such code is calling srand(time(0)) to seed the std::rand PRNG.

You can refactor that manual code management using the preprocessor:

// 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() <<  
 ;
  return 1;
}
catch (...) {
  std::cerr << "abnormal termination.
";
  return 1;
}

These .inc files are neither headers nor implementation files. The exact file extension doesn t matter as long as you don t use something which is commonly used for headers or implementation files, such as .h, .hpp, .cc, .cpp, and so forth. You can generate global_objects.inc and main_init.inc based off file-naming conventions, using include guards so that dependencies may be included (just as include guards work for headers).

For example, both of these files correspond with myevent.hpp and would be placed alongside that header:

// 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

Note that if you only require static data initialization with constant-expressions, then that is preferred over all other techniques. The primary restriction for that initialization is not being able to make a function call (but it s actually more complex), so it doesn t apply in your case; this is the only kind of global variable initialization that C can do, if you want to find out more.

I use the "static register object" pattern quite a bit, but you must be aware of one big problem - you must ensure that the thing you are registering with, which itself is likely to be static, is created before the thing you are registering. As C++ does not guarantee the order of static construction between translation units, this can be problematic. One solution is to use the so called Meyer Singleton:

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

    ... 

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

As all references to the Registry must go through the Instance() method, you are guaranteed the required construction order.





相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签