When and how are const variables initialized in C/C++? I am curious about particular types:
1) const static member of a class
2) function const local variable
3) const global variable
I mean of course application run time not source code way of initializing them.
1) const static member of a class
If it's a simple type initialised with a constant expression, then it's initialised during the static initialisation phase, before any user code is run.
Otherwise, if it's a class type with a non-trivial constructor, or has a non-constant initialiser, then it's initialised during the dynamic initialisation phase. This will happen before any function that's defined in the same translation unit is called; but there are potential deathtraps:
It might not happen before other static variables are initialised; you'll get evil bugs if their constructors access it.
It might not happen before main begins, if it's defined in a different unit.
It might not happen at all, if you never call any function in the same unit.
2) function const local variable
If it's static, then it's initialised the first time the program reaches its definition. If it's automatic, it's initialised every time the program reaches it, and destroyed when it goes out of scope.
3) const global variable
Same as the first. Both have static storage duration, and so are initialised and destroyed according to the same rules.
Notes:
Since you're asking about two different languages: in C, there's no such thing as "dynamic initialisation", and all non-local static variables are initialised before any user code runs.
Being const has no effect on when or how a variable is initialised.
Storage duration tells you what rules apply for when the variable in a program is allocated and deallocated. To answer your question is to specify storage duration for each case that you mentioned.
1) const static member of a class : static storage duration,
simple type - static initialisation,
class with non-trivial constructor - dynamic
initialisation
2) function const local variable : automatic storage duration ( but static
duration if it is static const),
automatic variable is initialized each time
the code is run,
static variable is initialized the first
time code is run,
3) const global variable : static storage duration
simple type - static initialisation,
class with non-trivial constructor - dynamic
initialisation
Explanation of storage durations:
automatic storage duration
The variable is allocated at the beginning of the enclosing code block
and deallocated on end. All non-global variables have this storage
duration, except those declared static, extern or thread_local.
static storage duration
The variable is allocated when the program begins and deallocated when
the program ends. Only one instance of the variable exists. All global
variables have this storage duration, plus those declared with static
or extern.
thread storage duration
The variable is allocated when the thread begins and deallocated when
the thread ends. Each thread has its own instance of the variable.
Only variables declared thread_local have this storage duration.
thread_local can appear together with static or extern to adjust
linkage. (since C++11)
dynamic storage duration
The variable is allocated and deallocated per request by using dynamic
memory allocation functions.
Related
Given this class in the header file:
class ClassA
{
public:
ClassA(){};
}
Then in file.cpp
#include file.h
ClassA* GlobalPointerToClassAType = new ClassA();
a. Is it allowed, and is it good practice to use the keyword 'new' to allocate memory for an object in the heap(?) in lines of file-scope?
b. If it is allowed, then when exactly does the constructor ClassA() is actually called?
c. How does it differ if I wrote instead this line:
ClassA GlobalInstanceOfClassAType = ClassA();
in terms of the time of calling the constructor, in terms of memory efficiency, and in terms of good practice?
a. Is it allowed, and is it good practice to use the keyword 'new' to allocate memory for an object in the heap(?) in lines of file-scope?
It is allowed. Whether is it good practice to use new here is opinion based. And i predict that most people will answer no.
b. If it is allowed, then when exactly does the constructor ClassA() is actually called?
Let's start from some concepts.
In C++, all objects in a program have one of the following storage durations:
automatic
static
thread (since C++11)
dynamic
And if you check the cppreference, it claim:
static storage duration. The storage for the object is allocated when the program begins and deallocated when the program ends. Only one instance of the object exists. All objects declared at namespace scope (including global namespace) have this storage duration, plus those declared with static or extern. See Non-local variables and Static local variables for details on initialization of objects with this storage duration.
So, GlobalPointerToClassAType has static storage duration, it fit the statement that "All objects declared at namespace scope (including global namespace) have this storage duration...".
And if you get deeper into the link of the above section, you will find:
All non-local variables with static storage duration are initialized as part of program startup, before the execution of the main function begins (unless deferred, see below). All non-local variables with thread-local storage duration are initialized as part of thread launch, sequenced-before the execution of the thread function begins. For both of these classes of variables, initialization occurs in two distinct stages:
There's more detail in the same site, you can go deeper if you want to get more, but for this question, let's only focus on the initialization time. According to the reference, The constructor ClassA() might be called before the execution of the main function begins (unless deferred).
What is "deferred"? The answer is in the below sections:
It is implementation-defined whether dynamic initialization happens-before the first statement of the main function (for statics) or the initial function of the thread (for thread-locals), or deferred to happen after.
If the initialization of a non-inline variable (since C++17) is deferred to happen after the first statement of main/thread function, it happens before the first odr-use of any variable with static/thread storage duration defined in the same translation unit as the variable to be initialized. If no variable or function is odr-used from a given translation unit, the non-local variables defined in that translation unit may never be initialized (this models the behavior of an on-demand dynamic library). However, as long as anything from a translation unit is odr-used, all non-local variables whose initialization or destruction has side effects will be initialized even if they are not used in the program.
Let's see a tiny example, from godbolt. I use clang, directly copy your code, except that the Class A and main are defined in the same translation unit. You can see clang generate some section like __cxx_global_var_init, where the class ctor is called.
Question coming from another question I recently asked Referencing a possibly destroyed static object. Can a destructor for an instance of a class use a global primitive variable/constant? Is it guaranteed to hold its value till program termination (i.e. after the statics have been destroyed)?
I know that static object destruction across compilation units is not defined but I was wondering whether the same holds for primitive values.
For example
Something.cpp
extern bool global_boolean_value;
Something::~Something() {
assert(global_boolean_value);
}
With respect to initialization the standard lumps together everything with "static storage duration", which are the variables (including member variables) declared using the static keyword (variables with internal linkage) as well as "true globals" (variables with external linkage). The standard does not distinguish between plain old data types one one hand and structs or classes on the other.
Note after comments (all quotes from the 2012 standard):
3.7.1 Static storage duration [basic.stc.static]
1 All variables which do not have dynamic storage duration, do not have thread storage duration, and are not local have static storage duration. The storage for these entities shall last for the duration of the program (3.6.2, 3.6.3).
(Emphasis by me.) There is no distinction between PODs and non-PODs. Note that the paragraph defines the lifetime of the storage, not of the (typed, initialized) object. When the program (not main()!) starts, all static storage is readily allocated. And yes, the destructors of objects with static storage duration are part of the program. This makes it, as far as I can see, safe to access errno in a destructor.
As you say, there is no guarantee about the order of initialization of objects with static storage duration across different translation units; within one TU the initialization is performed in the definition order, and destruction is then done in the opposite order.
That is all there is to it (apart from workarounds, like putting your globals in an artificial class or returning static local variables from functions).
The answer to your example question then depends on the storage duration of the object for which the destructor is called. If the object has static storage duration and is in a different translation unit, you are out of luck. If it is automatically or dynamically allocated, or if it is defined in the same translation unit after the global variable you are fine.
I read several posts on C++ initialization from Google, some of which direct me here on StackOverflow. The concepts I picked from those posts are as follows:
The order of initialization of C++ is:
Zero Initialization;
Static Initialization;
Dynamic Initialization.
Static objects (variables included) are first Zero-initialized, and then Static-initialized.
I have several inquiries as to the initialization issue (storage class issue may be related as well):
Global objects (defined without static keyword) are also static objects, right?
Global objects are also initialized like static objects by two steps like above, right?
What is the Static Initialization? Does it refer to initializing static objects (defined with static keyword)?
I also read that objects defined within block (i.e. in a function) with static keyword is initialized when the execution thread first enters the block! This means that local static objects are not initialized before main function execution. This means they are not initialized as the two steps mentioned above, right?
Dynamic initialization refers to initialization of objects created by new operator, right? It might refer to initialization like myClass obj = myClass(100); or myClass obj = foo();
I have too many inquiries on the initialization and storage class specifier issues. I read the C++2003 Standard document, but cannot find a clear logic since they are scattered throughout the document.
I hope you give me an answer that logically explains the whole map of storage class specifier and initialization. Any reference is welcome!
Code that might explain my question:
class myClass{
public:
int i;
myClass(int j = 10): j(i){}
// other declarations
};
myClass obj1;//global scope
static myClass obj2(2);//file scope
{ //local scope
myClass obj3(3);
static myClass obj4(4);
}
EDIT:
If you think my question is rather tedious, you can help explain your ideas based on the code above.
I read several posts on C++ initialization from Google, some of which direct me here on StackOverflow. The concepts I picked from those posts are as follows:
The order of initialization of C++ is:
Zero Initialization;
Static Initialization;
Dynamic Initialization.
Yes, indeed there are 3 phases (in the Standard). Let us clarify them before continuing:
Zero Initialization: the memory is filled with 0s at the byte level.
Constant Initialization: a pre-computed (compile-time) byte pattern is copied at the memory location of the object
Static Initialization: Zero Initialization followed by Constant Initialization
Dynamic Initialization: a function is executed to initialize the memory
A simple example:
int const i = 5; // constant initialization
int const j = foo(); // dynamic initialization
Static objects (variables included) are first Zero-initialized, and then Static-initialized.
Yes and no.
The Standard mandates that the objects be first zero-initialized and then they are:
constant initialized if possible
dynamically initialized otherwise (the compiler could not compute the memory content at compile-time)
Note: in case of constant initialization, the compiler might omit to first zero-initialized memory following the as-if rule.
I have several inquiries as to the initialization issue (storage class issue may be related as well):
Global objects (defined without static keyword) are also static objects, right?
Yes, at file scope the static object is just about the visibility of the symbol. A global object can be referred to, by name, from another source file whilst a static object name is completely local to the current source file.
The confusion stems from the reuse of the world static in many different situations :(
Global objects are also initialized like static objects by two steps like above, right?
Yes, as are local static objects in fact.
What is the Static Initialization? Does it refer to initializing static objects (defined with static keyword)?
No, as explained above it refers to initializing objects without executing a user-defined function but instead copying a pre-computed byte pattern over the object's memory. Note that in the case of objects that will later be dynamically initialized, this is just zero-ing the memory.
I also read that objects defined within block (i.e. in a function) with static keyword is initialized when the execution thread first enters the block! This means that local static objects are not initialized before main function execution. This means they are not initialized as the two steps mentioned above, right?
They are initialized with the two steps process, though indeed only the first time execution pass through their definition. So the process is the same but the timing is subtly different.
In practice though, if their initialization is static (ie, the memory pattern is a compile-time pattern) and their address is not taken they might be optimized away.
Note that in case of dynamic initialization, if their initialization fails (an exception is thrown by the function supposed to initialize them) it will be re-attempted the next time flow-control passes through their definition.
Dynamic initialization refers to initialization of objects created by new operator, right? It might refer to initialization like myClass obj = myClass(100); or myClass obj = foo();
Not at all, it refers to initialization requiring the execution of a user defined function (note: std::string has a user-defined constructor as far as the C++ language is concerned).
EDIT: My thanks to Zach who pointed to me I erroneously called Static Initialization what the C++11 Standard calls Constant Initialization; this error should now be fixed.
I believe there are three different concepts: initializing the variable, the location of the variable in memory, the time the variable is initialized.
First: Initialization
When a variable is allocated in memory, typical processors leave the memory untouched, so the variable will have the same value that somebody else stored earlier. For security, some compilers add the extra code to initialize all variables they allocate to zero. I think this is what you mean by "Zero Initialization". It happens when you say:
int i; // not all compilers set this to zero
However if you say to the compiler:
int i = 10;
then the compiler instructs the processor to put 10 in the memory rather than leaving it with old values or setting it to zero. I think this is what you mean by "Static Initialization".
Finally, you could say this:
int i;
...
...
i = 11;
then the processor "zero initializes" (or leaves the old value) when executing int i; then when it reaches the line i = 11 it "dynamically initializes" the variable to 11 (which can happen very long after the first initialization.
Second: Location of the variable
There are: stack-based variables (sometimes called static variables), and memory-heap variables (sometimes called dynamic variables).
Variables can be created in the stack segment using this:
int i;
or the memory heap like this:
int *i = new int;
The difference is that the stack segment variable is lost after exiting the function call, while memory-heap variables are left until you say delete i;. You can read an Assembly-language book to understand the difference better.
Third: The time the variable is initialized
A stack-segment variable is "zero-initialized" or statically-initialized" when you enter the function call they are defined within.
A memory-heap variable is "zero-initialized" or statically-initialized" when it is first created by the new operator.
Final Remark
You can think about static int i; as a global variable with a scope limited to the function it is defined in. I think the confusion about static int i; comes because static hear mean another thing (it is not destroyed when you exit the routine, so it retains its value). I am not sure, but I think the trick used for static int i; is to put it in the stack of main() which means it is not destroyed until you exit the whole program (so it retains the first initialization), or it could be that it is stored in the data segment of the application.
I'm not interested in having a static member cluttering up my header as it is only to be used by a free functions in the cpp body. If I declare a static variable here will it only ever occupy the one address just as for a static member?
Apologies but the myriad uses of static in C++ have left me quite unsure.
I am thinking along the lines of:
static Osp::Base::Runtime::Monitor sharedMonitor;
Yes. If you define a static variable at namespace scope, then there is one instance of that variable, accessible within the translation unit in which it's defined.
First, the word "static" has two meanings in C++: it can refer to the
keyword static (which in turn has different effects according to where
it is used), or it can refer to the lifetime of a variable: all
variables defined at namespace have static lifetime.
From what you say, I think you are looking for a variable with static
lifetime, which would not be visible outside the single translation unit
where it appears. The preferred way of doing this is to define a
variable in an unnamed namespace:
namespace {
int myWhatever; // No keyword static...
}
Class member variables which are declared static also have static
lifetime, as do local variables (inside a function) which are declared
static.
Such a variable is accessible everywhere after its definition in the
translation unit, but no where else. It has a single instance, which
comes into being at the start of the program, and lasts for as long as
the program runs. If it has a constructor, the constructor will be
called before main (or when the dynamic object is loaded, if dynamic
linking is used), and it's destructor will be called after exit has
been called (or when the dynamic object is unloaded, if dynamic linking
is used).
With regards to threads, C++11 has a storage class specifier
thread_local: variables declared with this specifier have one instance
per thread, with a lifetime which is the equivalent of that of the
thread. It will be initialized (constructed) before first use, and
destructed when the thread exits. This is something different from
static.
In VC++2008 there is a serious difference in initialization of static local variable and static local object. Static local variable is initialized before main() and its definition statement within the function is skipped. Static local object is initialized by 0 value before main() and its definition statement within the function is executed only once . Constructor is started and object is initialized by appropiate value. All that can be seen in Debug mode. Does this solution correspond to the existing C++ Standard?
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf :
Every object of static storage
duration shall be zero-initialized at
program startup before any other
initialization takes place. [ Note: in
some cases, additional initialization
is done later. —end note ]