【问题标题】:Populate a static member container in c++在 C++ 中填充静态成员容器
【发布时间】:2009-04-23 08:35:13
【问题描述】:

我有一个静态类成员,它是一些容器,比如

(Foo.h)

class Foo
{
   ...
private:
   static list<string> s_List;
}

我需要用一些特定值填充列表。实际上它也应该是 const,但这可能会使问题进一步复杂化。 所有的类成员函数都是静态的,所以在构造函数中初始化它没有意义。

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

    一个常见的解决方案是这样做:

    // header
    class Foo
    {
    ...
    private:
       static list<string> s_List;
    }
    
    // cpp
    list<string> init()
    {
         list<string> tmp;
         ... fill tmp with strings
    
         return tmp;
     }
    
     list<string> Foo::s_List(init());
    

    另一种方法就像 Neil Butterworth 建议的那样。

    【讨论】:

    • 我猜你的解决方案是一个更通用的解决方案,因为可以毫不费力地初始化私有成员......并且 s_List 是否可能是 const ?然后 init() 也应该返回 const list 。对吗?
    • 另外,从性能的角度来看:返回对 tmp 的引用不是更好吗,因为无论如何它都会被隐式复制构造函数(或赋值运算符,纠正我)复制最后一行?那我们就不会被复制两次了。有没有我没有计算在内的小细节?
    • 是的,您可以将其更改为 const 列表。但是你不能返回一个引用,因为“tmp”是一个局部变量,一旦你离开 init() 就会死掉。
    • 您可以做的是将“tmp”更改为静态,然后返回对它的引用。但是,如果您使用 init() 来初始化多个列表,这会使事情变得复杂,而且我认为这有点矫枉过正(除非您推送很多字符串,然后可能首选另一种方法)。
    【解决方案2】:

    另一种选择是创建一个简单的初始化器类:

    list <string> Foo::s_List;
    
    struct Init {
       Init() {
          Foo::s_List.insert("apple");
          Foo::s_List.insert("bannana");
          Foo::s_List.insert("grapes");
       }
    };
    
    static Init doInit;
    

    请注意,由于列表是私有的,这可能需要您将 Init 设为 Foo 的朋友。将这些类包含在它们正在初始化的类中通常也很方便。

    但是,我刚刚重新阅读了您的问题,然后又出现了另一个想法 - 如果列表是 const,您可能不会更改它,在这种情况下,使用按排序顺序初始化的字符串的简单数组可能是更好的解决方案。搜索(使用 std::binary_search)肯定会比列表更快,当然也可以很容易地初始化。

    【讨论】:

    • 好主意。它可以保证工作,因为静态成员和全局变量在每个源文件中按顺序初始化。由于该列表在原始问题中是私有的,因此需要一个朋友声明。
    • Neil Butterworth:至于你的最后一次编辑,至少在我的情况下这不是最好的解决方案,因为我需要检查列表中是否有一些字符串,所以我最好使用标准包含/查找算法。
    • 您可以在数组上使用标准算法 - 实际上在排序后的数组上使用 std::binary_search 会非常快
    • AFAIK C++ 不保证全局变量分配顺序。您如何确定 doInit 将在 s_List 之后分配?
    • 另一种避免复制的方法是将 s_List 设为 shared_ptr。
    【解决方案3】:

    如果你的编译器支持 C++0x,这实际上是微不足道的。

    #include <iostream>
    #include <list>
    
    class Foo
    {
    public:
       static std::list<std::string> s_List;
    };
    
    std::list<std::string> Foo::s_List = {"hello", "world", "asdf", "qwerty"};
    
    int main()
    {
        for(const std::string& str : Foo::s_List)
            std::cout << str << std::endl;
    
        return 0;
    }
    

    这适用于 const 和非 const 静态成员。我已经用 clang-4.2、gcc-4.7、gcc-4.6 和 gcc-4.5 测试了这个 sn-p。 Gcc-4.5 不支持更新的 for 语法,因此您必须使用带有迭代器的传统 for 循环。另外,不要忘记将 -std=c++0x 标志传递给编译器。我相当有信心 Visual Studio 也支持这一点,但我不确定也不知道哪些版本。

    【讨论】:

      【解决方案4】:

      这取决于您需要在该列表中放入哪些值。它们是静态的还是需要某种形式的计算?

      如果它们是静态的,您可以这样做:

      namespace {
         const char* const initVals[] = { "A", "B", "C" };
      }
      
      list<string> Foo::s_list(initVals, initVals + 3);
      

      【讨论】:

        【解决方案5】:

        一种可能的解决方案是使用访问器方法来检查它是否已初始化,如果未初始化则执行此操作。

        【讨论】:

          【解决方案6】:

          我(问题的​​作者)尝试这样做的方法是徒劳的。


          我试图做类似的事情(在 Foo.cpp 中):

          list<string> Foo::s_List = list<string>();
          Foo::s_List.insert("apple");
          Foo::s_List.insert("bannana");
          Foo::s_List.insert("grapes");
          

          但这会导致编译器错误。


          然后我想到了创建一个 Initialize() 方法并直接从代码中调用它

          void Foo::Initialize()
          {
              s_List.insert("rats");
              s_List.insert("cats");
          }
          
          Foo::Initialize(); 
          

          // 错误:编译器认为这是对方法的重新定义,而不是调用。


          剩下的唯一可行的想法(尚未尝试过)是检查列表是否在每个使用它的方法中为空,如果是,则调用 Initialize()。但这很丑!

          【讨论】:

          • 初始化应该是一个静态函数。很抱歉这个问题,但您是否尝试在外部函数或方法进行函数调用?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-20
          • 2011-04-01
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多