Static member allocation

I see many time where static data member is used to count creations of objects -

i.e.

1. the static data member is init to 0

2. the static data member is incremented by 1, in the Class' constructor, every time an object is created

However, if you define a global object of a class,

How can you tell that the static data member is initialized BEFORE the constructor of the global object is called? (i.e. before the global object is created).

Because to my understanding, you do not know in advance the order of global objects' creation -

so the Global Object could be created BEFORE the static data member was created and initialized.
Last edited on
In your class' .h file, declare the static variable inside your class like so:
1
2
3
4
5
class myClass
{
   private:
   static int count;
};


Then in your class' .cpp file, put the following:
int myClass::count = 0;

This will initialize the count at the start of the program, before any objects get created.
> How can you tell that the static data member is initialized BEFORE the constructor of the global object is called?

Let us say, we have:

1
2
3
4
5
struct my_class // in a header
{
    A() ; // constructor (dynamic initialization)
    static int cnt ;
};


And int my_class::cnt ; // default initialization in some cpp file

my_class::cnt would be initialized with a value of zero before any constructor of my_class is called.
Variables with static storage duration or thread storage duration shall be zero-initialized before any other initialization takes place.



If, instead, we had:
int my_class::cnt = 5 ; // constant initialization

my_class::cnt would be initialized with a value of five before any constructor of my_class is called.
Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.



If, instead, we had:
int my_class::cnt = std::rand() ; // dynamic initialization

There is no guarantee that dynamic initialization of my_class::cnt would have taken place before the constructor of an object (in another tanslation unit at namespace scope) is called.
@ JLBorges Freddy
Thank you very much!

JLBorges
Thanks a lot again!

Regarding Dynamic Initialization,
The following global object rect1 is also dynamically initialized?
even though all its data members initialization values are known at compile time?

I ask as I'm not totally sure what you mean by "dynamic initialization", I read on it in several places but it seems confusing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Counter
{
public:
   static unsigned int m_cnt;
}

unsigned int UniqueID::m_ID(0);

class Rectangle
{
public:
   Rectangle();
private:
   m_width;
   m_height;
}

Rectangle::Rectangle()
{
   m_width = m_height = 0;
   Counter::m_cnt++;
}


Rectangle rect1;

int main ()
{
    cout << "hello";
    return 0;
}
Last edited on
> The following global object rect1 is also dynamically initialized?

Yes.


> I'm not totally sure what you mean by "dynamic initialization"

A simple way to look at dynamic initialization is:
any initialization where the initializer expression is evaluated at run-time is dynamic initialization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// namespace scope
int a ; // static initialization (zero initialized)

const int b = 5 * 22 ; // static initialization (constant initialization; 5*22 is evaluated at compile time)

int c = b + 5 ; // static initialization (constant initialization; b+5 is evaluated at compile time)

int d = ++c ; // dynamic initialization (initialization changes the value of another object)

inline int foo( int arg ) { return arg+5 ; }

int e = foo(5) ; // unspecified (left to the implementation). 
                 // may be static or may be dynamic initialization
                 // requirement: if statically initialized, the initialization must produce
                 // the same value as would haven been produced by dynamic initialization.

constexpr int bar( int arg ) { return arg+5 ; }

int f = bar(5) ; // static initialization (constant initialization; bar(5) is evaluated at compile time)

int g = bar(++c) ; // dynamic initialization (initialization changes the value of another object)

int h = std::rand() ; // dynamic initialization (std::rand() is evaluated at runtime)

class Rectangle
{
    public:
        constexpr Rectangle( int a = 8, int b = 4 );
    private:
        int m_width;
        int m_height;
};

constexpr Rectangle::Rectangle( int a, int b ) : m_width(a), m_height(b)
{ /* constexpr constructor must have an empty body */ }

Rectangle r1 ; // static initialization

Rectangle r2( 20, 30 ) ; // static initialization

Rectangle r3( 20, std::rand() ) ; // dynamic initialization

Rectangle r3( 20, ++c ) ; // dynamic initialization 


http://en.cppreference.com/w/cpp/language/initialization
Last edited on
@JLBorges

Thank you very very much!

I think I got it thanks to your awesome post :)

To make sure please,

I removed (by commenting out) one line from the Constructor of Rectangle class.

Is rect1 now statically initialized?

if yes, you can't actually tell which will be initialized first, the static Counter::m_cnt or global rect1, right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Counter
{
public:
   static unsigned int m_cnt;
}

unsigned int UniqueID::m_ID(0);

class Rectangle
{
public:
   Rectangle();
private:
   m_width;
   m_height;
}

Rectangle::Rectangle()
{
   m_width = 0;
   m_height = 0;
   // Counter::m_cnt++;  *** commented out ***
}


Rectangle rect1; //rect1 is global object of class Rectangle, therefore resides in static memory

int main ()
{
    cout << "hello";
    return 0;
}
Last edited on
I added something to my earlier post; you may not have seen the added part when you made your post.


> Is rect1 now statically initialized?

An implementation is allowed (but not required) to initialize rect1 statically.

For instance, see:
1
2
3
4
rect1:
	.long	25                      # 0x19 ; statically initialized with 25
	.long	25                      # 0x19 ; statically initialized with 25
	.size	rect1, 8

in: http://coliru.stacked-crooked.com/a/62ec2235d6c10300


> you can't actually tell which will be initialized first, the static Counter::m_cnt or global rect1, right?

Yes. Example from the standard:
For example,
1
2
3
4
5
6
7
8
9
10
inline double fd() { return 1.0; }

extern double d1;

double d2 = d1; // unspecified:
                // may be statically initialized to 0.0 or
                // dynamically initialized to 0.0 if d1 is
                // dynamically initialized, or 1.0 otherwise

double d1 = fd(); // may be initialized statically or dynamically to 1.0 
Last edited on
@JLBorges

Thank you very much again!

I got you.

One note please, regarding constexpr constructor, vs not-constexpr constructor.

As you said, r1 and r2 will be statically initialized:
1
2
3
4
5
6
constexpr Rectangle::Rectangle( int a, int b ) : m_width(a), m_height(b)
{ /* constexpr constructor must have an empty body */ }

Rectangle r1 ; // static initialization

Rectangle r2( 20, 30 ) ; // static initialization 
*BTW, not you must define empty-argument default constructor, for r1 definition? (otherwise compiler produces an error).


If you write the same code, but remove the constexpr keyword, i.e.
1
2
3
4
5
6
7
8
Rectangle::Rectangle( int a, int b )
{
   m_width = a;
   m_height =  b;
}
Rectangle r1 ; // static initialization?

Rectangle r2( 20, 30 ) ; // static initialization? 


will r2 still be statically initialized?
Last edited on
> BTW, not you must define empty-argument default constructor, for r1 definition?
> (otherwise compiler produces an error).

For Rectangle r1 ; to work, rectangle must be DefaultConstructible.
It must have either a constructor that takes no arguments or a constructor where every argument has a default value.


> If you write the same code, but remove the constexpr keyword,
> will r2 still be statically initialized?

r2 may be statically initialized or it may be dynamically initialized; it is left unspecified.
An implementation is allowed to (but not required to) initialize r2 statically.
(In every implementation that I have checked, with optimizations enabled, r2 would be statically initialized).

From the cppreference page earlier linked to:
The compilers are allowed to initialize dynamically-initialized variables as part of static initialization (essentially, at compile time), if both of these are true:

1) the dynamic version of the initialization does not change the value of any other object of namespace scope prior to its initialization

2) the static version of the initialization produces the same value in the initialized variable as would be produced by the dynamic initialization if all variables not required to be initialized statically were initialized dynamically.
Last edited on
@JLBorges

Got you, thank you very very much! :)

what do you mean by 'an implementation is allowed' in:
r2 may be statically initialized or it may be dynamically initialized; it is left unspecified.
An implementation is allowed to (but not required to) initialize r2 statically.



Also, what do you mean by namespace scope?

I know what namespace is, just not sure what is the meaning of it here.
An implementation is the way that a specific compiler/vendor implements the C++ language, based on the set rules provided by the C++ standard. Basically, that quote is saying that some compilers will initialize r2 statically, but others will not, it depends on the compiler.

Namespace scope is simply that: The scope within a namespace. A scope is normally formed between two curly braces, they are variables that can only be accessed from within that scope. Though of course, in some cases (notably namespace scope) you can still access the variable through the use of the scope resolution operator ('::').
Last edited on
> what do you mean by 'an implementation is allowed' in:

A conforming implementation may have decided to initialize r2 statically.
Another conforming implementation may have decided to initialize r2 dynamically.
Both are conforming implementations.


> what do you mean by namespace scope?

1
2
3
4
5
6
7
8
9
10
11
12
13
// file a.cc

int i = 9 ; // namespace scope

namespace A
{
    int j = 10 ; // namespace scope
}

void foo() 
{
     static int k = 12 ; // not at namespace scope
}

Thank you JLBorges :)


so by namespace scope you mean file-global scope or namespace-global scope (i.e. objects defined inside namespace, there if weren't the namespace, they were global).

e.g.
1
2
3
4
5
6
7
8
9
10
11
12
//file b.cpp
namespace B
{
   class Boarding
{
public:
   const char& getTime()
private:
   char *m_time; //not at namespace scop
} boarding; //namespace scop

> by namespace scope you mean file-global scope or namespace-global scope
> (i.e. objects defined inside namespace, there if weren't the namespace, they were global).

Yes. The outermost region in which names can be declared is also a namespace (the global namespace). Names declared here are at namespace scope (called the global namespace scope, which is sometimes referrred to by its abbreviation: global scope)

Scopes may be nested:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// file b.cpp

namespace B // 'B' is at namespace scope
{
    class Boarding // 'Boarding' is at namespace scope
    {
        public:
            char getTime() ; // 'getTime' is at class scope
        private:
            char *m_time; // 'm_time' is at class dscope
    } boarding; // 'boarding' is at namespace scope

    char Boarding::getTime()
    {
        int x = 7 ; // 'x' is at block scope

        struct y // 'y' is at block scope
        {
            int a = 8 ; // 'a' is at class scope
            enum class colour_t { WHITE, GREY, BLACK } ; // 'colour_t' is at class scope
                                                          // and 'GREY' etc. are at enum scope
        };

        y yy ; // 'yy' is at block scope
        return x + yy.a + int( y::colour_t::GREY ) ;
    }

    namespace C // 'C' is at namespace scope
    {
        int i = 89 ; // 'i' is at namespace scope

        void foo( int arg ) // 'foo' is at namespace scope
                             // 'arg' is at block scope
        {
            int j = 23 ; // 'j' is at block scope
            
            label: // 'label' is at function scope
                {
                    static int k = 45 ; // 'k' is at block scope
                }
        }
    }
}
JLBorges

Thanks a lot friend!
Topic archived. No new replies allowed.