C++:静态初始化数组成员,逐个成员初始化

3

我现在可以在全局范围内做到这一点,一切都正常:

const char* Foo::bars[3] = {"a", "b", "c"};

但我想这样做是因为这样更清晰和自文档化(特别是如果您使用枚举作为索引):

const char* Foo::bars[3];
bars[0] = "a";
bars[1] = "b";
bars[2] = "c";

有没有任何可能?

我知道我可以在函数内部完成这个操作(例如,类的构造函数),但是如果构造函数不在程序开始时被调用,而我想使用静态数组呢?这会导致问题。


2
构造函数为什么不应该被调用? - Simone
OP 想要将 Foo::bars 设置为静态成员,并且使其元素在静态地初始化(在 main 函数执行之前)。 - Emile Cormier
1
@Emile 是的,如果 bars 被声明为静态,那么 OP 的第一行代码就会实现这一点。构造函数将被调用,所以我错过了整个讨论的重点。 - Simone
@Simone,楼主的意思是,作为静态初始化,他提供的声明本身就保证了它会被初始化。如果它依赖于另一个类的构造函数,则必须实例化该类以保证此列表的初始化。显然,如果那个类也是静态的,那就不用担心了,但值得一提的是这里有一个额外的链接。 - Moo-Juice
5个回答

7

这个怎么样?

const char* Foo::bars[3] = {
/* Index    Value */
/* 0 */     "a",
/* 1 */     "b",
/* 2 */     "c"
};

我经常使用这种“技巧”来使结构体数组的初始化看起来像是一个自说明的表格。

如果“构造函数没有被调用”,它就不起作用,因为它与 OP 的代码相同。无论如何,不要问我为什么不应该被调用... - Simone
@Simone:OP希望Foo::bars成为静态成员,并希望它们在程序运行之前初始化。但是OP不喜欢初始化语法。是的,我的代码是一样的,但我添加了内联注释,以使每个数组元素的数组索引可见。粗糙,但在我看来很有效。 - Emile Cormier
当你在其中有枚举常量时,这将变得更加有用,但如果你的枚举由于某种原因改变顺序,则仍需要重新组织数组初始化。 - Andrew Noyes

1
在C++中没有等同于Java中的static块。
如果你真的想要自动初始化数组,可以创建一个简单的类来完成这个工作:
// in .cpp
class FooInitializer {
public:
    FooInitializer() {
        Foo:bars[0] = "a";
        Foo:bars[1] = "b";
        Foo:bars[2] = "c";
    }
};

static FooInitializer fooInitializer;

0
你可以使用一个访问器函数:
const char* GetArray()
{
    static char* result[3];
    static isInitialized = false;
    if (!isInitialized)
    {
        result[0] = "a";
        result[1] = "b";
        result[3] = "c";
        initialized=true;
    }
    return result;
}

“a”,“b”和“c”字面量具有局部作用域吗?我认为这可能会导致悬空指针。 - Emile Cormier
我做了一些研究...字符串字面量确实具有全局作用域。 - Emile Cormier
请注意,此解决方案不是线程安全的,因为该函数不可重入。 - Emile Cormier
1
这也是我使用字面量声明std::map的最爱解决方案,比如说你想创建一个翻译表之类的东西。这些事情必须在运行时发生。我觉得OP可能正在寻找更像Python字典语法的东西,但不幸的是,在C++实现中,细节问题会阻碍它的实现。值得注意的是,C++0x将为初始化容器添加语法。 - Andrew Noyes
@Emile,你可以使用标准技术(如互斥锁)使其线程安全。 - Tobias Langner

0
这里有另一种使用Singleton模式的解决方案。请注意,数组在单例构造函数中只初始化一次。还要注意,这不是线程安全的。同时要小心单例的邪恶(在本网站上搜索“singleton”以找到有关此问题的一些有趣辩论)。
#include <iostream>

class StringTable
{
public:
    enum StringId
    {
        hello,
        bye,
        goodDay,
        stringCount
    };

    static const char* lookup(StringId id) {return instance().table_[id];}

private:
    StringTable()
    {
        table_[hello] = "Hello World!\n";
        table_[bye] = "Goobye, cruel world!\n";
        table_[goodDay] = "I said good day!\n";
    }

    static StringTable& instance()
    {
        static StringTable theInstance;
        return theInstance;
    }

    const char* table_[stringCount];
};


int main()
{
    std::cout << StringTable::lookup(StringTable::hello)
              << StringTable::lookup(StringTable::bye)
              << StringTable::lookup(StringTable::goodDay);
}

0

你可能想在 bars 中加上额外的 const

const char* const Foo::bars[3] = 
{
   "a",
   "b",
   "c"
};

按照您的声明方式,您实际上可以逐个设置成员来完成您想要的操作,尽管您需要使用一个“init”函数来完成它。

如果您确实希望将其设置为const,这可能更可取,那么以后逐行分配它们甚至在某种“init”方法中也将变得非法,并且您应该简单地使用代码布局使其更清晰明了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接