2

我知道在 C++ 函数中用 'static' 修饰符声明的变量只初始化一次,我想要做的是用适当的内容初始化静态动态分配的数组。这是我的代码片段:

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 ;
    ...
}

我当然知道我可以做这样的事情:

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

但这可能不是最佳的编码方式,并且可以以某种方式更有效地完成。我可以使用“静态”修饰符以某种方式进行更优化的实现吗?

4

4 回答 4

3

放弃指针并使用vector

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

好处是它可以清理自己(不再打电话了delete。)cha-ching!

于 2013-10-24T19:52:00.580 回答
1

如果您希望它预先填充零(看起来您确实如此),我能想到的最小更改是使用符合 C++11 的工具链对该数组进行值初始化。IE

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

关于它的工作原理,突出显示的部分适用于您的初始分配如何按照我描述的方式填充零。

C++11 § 8.5,p10

初始值设定项为空括号集的对象,即(),应进行值初始化

通过值初始化的定义:

C++11 § 8.5,p7

对 T 类型的对象进行值初始化意味着:

  • 如果 T 是具有用户提供的构造函数 (12.1) 的(可能是 cv 限定的)类类型(第 9 条),则调用 T 的默认构造函数(如果 T 没有可访问的默认构造函数,则初始化格式错误) ;

  • 如果 T 是没有用户提供的构造函数的(可能是 cv 限定的)非联合类类型,则该对象被零初始化,并且如果 T 的隐式声明的默认构造函数不平凡,则调用该构造函数。

  • 如果 T 是一个数组类型,那么每个元素都是值初始化的;

  • 否则,对象被零初始化。

这给我们带来了零初始化的对象类型意味着什么:

C++11 § 8.5,p5

对 T 类型的对象或引用进行零初始化意味着:

  • 如果 T 是标量类型(3.9),则将对象设置为值 0(零),作为整数常量表达式,转换为 T (103)

  • 如果 T 是(可能是 cv 限定的)非联合类类型,则每个非静态数据成员和每个基类子对象都被初始化为零**,并且填充被初始化为零位;

  • 如果 T 是(可能是 cv 限定的)联合类型,则对象的第一个非静态命名数据成员被零初始化,填充被初始化为零位;

  • 如果 T 是数组类型,则每个元素都初始化为零;

  • 如果 T 是引用类型,则不执行初始化。

103) 如 4.10 所述,将值为 0 的整型常量表达式转换为指针类型会导致空指针值。

于 2013-10-24T19:51:19.143 回答
0

在许多情况下,带有构造函数的全局变量(和函数局部静态变量)可能会导致很多意想不到的问题。随着程序规模和复杂性的增长,这些事情可能变得非常难以管理。

如果你明确地管理它们,你会做得更好——因为这样你就可以明确地控制构造/破坏的顺序以及这些事情发生的时间。

如果您使用上述建议的向量,则在程序退出时,该向量将被释放。但是,你不能直接控制发生这种情况的顺序,所以如果 getNextPass() 被作为其他正在清理的东西的一部分调用(所以,在 main() 返回之后),它可能会崩溃,你将不得不困惑找出为什么以及如何正确订购。

另请注意,函数本地静态初始化通常不是线程安全的。GCC 具有线程安全的初始化机制,但其他编译器(如 VC)没有。即使支持,它也不是免费的,可能需要启用选项。

手动执行(非常类似于编译器自动生成的代码):

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;
    }
    ...

}

澄清一点“使用构造函数”意味着需要执行代码的初始化。所以,“静​​态 int x = 5;” 没有,但“静态 int y = rand();” 做。

于 2013-10-24T20:05:40.537 回答
0

创建一个名为 AllocAndInit 的静态函数并像这样调用它(在函数内部分配并初始化您分配的数组):

static int *cur_pos = AllocAndInit(pass_len);

AllocAndInit 方法应如下所示:

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

    return ret;
}
于 2013-10-24T19:53:32.953 回答