简体   繁体   中英

How to make static initialization of a static dynamically allocated array in C++?

I know that variable which is declared with 'static' modifier in a C++ function is initialized only once and what I want to do is to initialize static dynamically allocated array with appropriate content. Here is my code fragment:

inline char* getNextPass()
{
    static int chars_num = chars_data.charset_len, pass_len = chars_data.pass_len ;
    static int *cur_pos = new int[pass_len] ;  // this is static variable in function, what means it's initialized only once

    ...
    for(int aa = 0; aa < pass_len; aa++)  // this is executed every time the function is called. How can I make this code execute only once ?
    cur_pos[aa] = 0 ;
    ...
}

I know of course that I could do something like this:

...
flag = true ;
...
inline char* getNextPass()
{
    ...
    if(flag)
    for(int aa = 0; aa < pass_len; aa++)
    cur_pos[aa] = 0 ;
    flag = false ;
    ...
}

but it's probably not optimal way of coding and can be done somehow more effectively. Can I use 'static' moddifier some way to make more optimized implementation ?

Ditch the pointer and use vector

static vector<int> cur_pos(pass_len, 0);

The benefit is that it cleans itself up (no more calling delete .) cha-ching!

If you want it prefilled with zeros (and it appears you do), the most-minimal change I can think of is to value-initialize that array with a C++11 compliant toolchain. Ie

static int *cur_pos = new int[pass_len](); // note the tail-parens.

Regarding why it works, highlighted portions applicable to how your initial allocation is filled with zeros if done as I describe.

C++11 § 8.5,p10

An object whose initializer is an empty set of parentheses, ie, () , shall be value-initialized .

By the definition of value initialization :

C++11 § 8.5,p7

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T's implicitly-declared default constructor is non-trivial, that constructor is called.

  • if T is an array type, then each element is value-initialized;

  • otherwise, the object is zero-initialized.

Which brings us to what it means for your object-type to be zero-initialized :

C++11 § 8.5,p5

To zero-initialize an object or reference of type T means:

  • if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T (103)

  • if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized** and padding is initialized to zero bits;

  • if T is a (possibly cv-qualified) union type, the object's first non-static named data member is zero- initialized and padding is initialized to zero bits;

  • if T is an array type, each element is zero-initialized;

  • if T is a reference type, no initialization is performed.

103) As specified in 4.10, converting an integral constant expression whose value is 0 to a pointer type results in a null pointer value.

Create a static function called AllocAndInit and call it like this (inside the function allocate and init the array you allocated):

static int *cur_pos = AllocAndInit(pass_len);

the AllocAndInit method should look like this :

int * AllocAndInit(pass_len)
{
    int * ret = new int[pass_len];
    for (int i = 0 ; i < pass_len; ++i)
    // init here ....

    return ret;
}

Global variables (and function local statics) with constructors can cause a lot of unexpected problems in many situations. As programs grow in size and complexity these things can get to be very hard to manage.

You will be better off if you manage them explicitly - because then you get explicit control over the order of construction/destruction and when these things happen.

If you use a vector as suggested above, then as the program exits the vector will be freed. But, you cannot directly control the order in which this happens, so if getNextPass() is called as part of something else which is being cleaned up (so, after main() returns), it will likely crash and you will have to puzzle out why and how to get the ordering correct.

Also note that function local static initialization is not thread-safe in general. GCC has a thread-safe initialization mechanism, but other compilers (like VC) do not. Even when supported it isn't free and may require an option to enable.

Doing it manually (very similar to the auto-generated code by the compiler):

inline char* getNextPass()
{
    static bool initialized;
    static int chars_num;
    static int pass_len;
    static int *cur_pos;
    if (!initialized) {
        chars_num = chars_data.charset_len;
        pass_len = chars_data.pass_len ;
        cur_pos = new int[pass_len];    

        for(int aa = 0; aa < pass_len; aa++)  
            cur_pos[aa] = 0 ;
        initialized = true;
    }
    ...

}

To clarify a bit "with constructors" means an initialization which requires code to execute to do. So, "static int x = 5;" does not, but "static int y = rand();" does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM