There is one caveat: if the variable is an object, its constructor is invoked every time it enters scope and is created,
and its destructor is invoked every time it goes out of scope.
// Inefficient implementation:
for (int i = 0; i < 1000000; ++i) {
Foo f; // My ctor and dtor get called 1000000 times each.
f.DoSomething(i);
}
It may be more efficient to declare such a variable used in a loop outside that loop:
Foo f; // My ctor and dtor get called once each.
for (int i = 0; i < 1000000; ++i) {
f.DoSomething(i);
}
Static and Global Variables
Static or global variables of class type are forbidden: they cause hard-to-find bugs due to indeterminate order of
construction and destruction.
Objects with static storage duration, including global variables, static variables, static class member variables, and
function static variables, must be Plain Old Data (POD): only ints, chars, floats, or pointers, or arrays/structs of POD.
The order in which class constructors and initializers for static variables are called is only partially specified in C++
and can even change from build to build, which can cause bugs that are difficult to find. Therefore in addition to
banning globals of class type, we do not allow static POD variables to be initialized with the result of a function,
unless that function (such as getenv(), or getpid()) does not itself depend on any other globals.
Likewise, the order in which destructors are called is defined to be the reverse of the order in which the constructors
were called. Since constructor order is indeterminate, so is destructor order. For example, at program-end time a
static variable might have been destroyed, but code still running -- perhaps in another thread -- tries to access it and
fails. Or the destructor for a static 'string' variable might be run prior to the destructor for another variable that
contains a reference to that string.
As a result we only allow static variables to contain POD data. This rule completely disallows vector (use C arrays
instead), or string (use const char []).
If you need a static or global variable of a class type, consider initializing a pointer (which will never be freed), from
either your main() function or from pthread_once(). Note that this must be a raw pointer, not a "smart" pointer, since
the smart pointer's destructor will have the order-of-destructor issue that we are trying to avoid.
Classes
Classes are the fundamental unit of code in C++. Naturally, we use them extensively. This section lists the main dos
and don'ts you should follow when writing a class.
Doing Work in Constructors
In general, constructors should merely set member variables to their initial values. Any complex initialization
should go in an explicit Init() method.
Definition:
It is possible to perform initialization in the body of the constructor.
Pros:
Convenience in typing. No need to worry about whether the class has been initialized or not.
Cons:
The problems with doing work in constructors are:
l
There is no easy way for constructors to signal errors, short of using exceptions (which are forbidden).
l
If the work fails, we now have an object whose initialization code failed, so it may be an indeterminate state.
l
If the work calls virtual functions, these calls will not get dispatched to the subclass implementations. Future
modification to your class can quietly introduce this problem even if your class is not currently subclassed,
causing much confusion.
l
If someone creates a global variable of this type (which is against the rules, but still), the constructor code will
be called before main(), possibly breaking some implicit assumptions in the constructor code. For instance,
gflags will not yet have been initialized.
Decision:
If your object requires non-trivial initialization, consider having an explicit Init() method. In particular, constructors
should not call virtual functions, attempt to raise errors, access potentially uninitialized global variables, etc.
Default Constructors
You must define a default constructor if your class defines member variables and has no other constructors.
Otherwise the compiler will do it for you, badly.
link
▽
link
▽
link
▽