【问题标题】:Initialize static variables in C++ class?在 C++ 类中初始化静态变量?
【发布时间】:2011-06-28 12:54:33
【问题描述】:

我注意到我在一个类中的一些函数实际上并没有访问该对象,所以我将它们设为static。然后编译器告诉我,他们访问的所有变量也必须是静态的——嗯,到目前为止,这是可以理解的。我有一堆字符串变量,例如

string RE_ANY = "([^\\n]*)";
string RE_ANY_RELUCTANT = "([^\\n]*?)";

在课堂上等等。然后我把它们都做成了static const,因为它们永远不会改变。但是,我的程序只有在我将它们移出类时才能编译:否则,MSVC++2010 会抱怨“只能在类中初始化静态常量整数变量”。

这很不幸。有解决方法吗?我想把他们留在他们所属的班级里。

【问题讨论】:

    标签: c++ static initialization


    【解决方案1】:

    它们不能在类内初始化,但可以在类外的源文件中初始化:

    // inside the class
    class Thing {
        static string RE_ANY;
        static string RE_ANY_RELUCTANT;
    };
    
    // in the source file
    string Thing::RE_ANY = "([^\\n]*)";
    string Thing::RE_ANY_RELUCTANT = "([^\\n]*?)";
    

    更新

    我刚刚注意到你的问题的第一行 - 你想要制作这些函数static,你想要制作它们const。将它们设为static 意味着它们不再与对象关联(因此它们无法访问任何非静态成员),而将数据设为静态意味着它将与该类型的所有对象共享。这很可能不是你想要的。让他们const 仅仅意味着他们不能修改任何成员,但仍然可以访问它们。

    【讨论】:

    • 它们不访问对象中的任何内容,只是作为参考参数提供给它们的临时变量。因此它们可能应该是conststatic
    • @Felix:这是不可能的,const 表示它不会修改this...并且static 方法没有this
    • @androidplusios.design:我不关注。你需要string,因为它是一个变量定义,需要一个类型说明符,类型是string。如果你愿意,你可以把()放在一个声明符周围,它不会改变意思。
    • @androidplusios.design:它确实看到了。但是你必须使用定义语法来定义和初始化它,并且包括一个类型说明符,无论它之前是否被声明过。这就是指定语言的方式。
    • @androidplusios.design:或者,如果你问为什么语法是这样的,以及它所有的不一致、冗余和歧义:因为它从更简单的语言演变了几十年。
    【解决方案2】:

    迈克·西摩已经给了你正确的答案,但要补充...
    正如编译器所说,C++ 只允许您在类主体中声明和定义静态常量整数类型。所以你实际上可以这样做:

    class Foo
    {
        static const int someInt = 1;
        static const short someShort = 2;
        // etc.
    };
    

    你不能对任何其他类型这样做,在这种情况下你应该在你的 .cpp 文件中定义它们。

    【讨论】:

      【解决方案3】:

      从 C++11 开始,它可以在带有 constexpr 的类中完成。

      class stat {
          public:
              // init inside class
              static constexpr double inlineStaticVar = 22;
      };
      

      现在可以通过以下方式访问该变量:

      stat::inlineStaticVar
      

      【讨论】:

      • 对于整数、双精度、指针等来说绝对不错。但是这不适用于问题中的字符串,因为字符串不是文字或引用。
      • 好点。不过,您可以使用constexpr char RE_ANY[] = "([^\\n]*)";constexpr std::string_view RE_ANY("([^\\n]*)", 9);
      【解决方案4】:

      静态成员变量必须在类中声明,然后在类外定义!

      没有解决方法,只需将它们的实际定义放在源文件中即可。


      从您的描述来看,您似乎没有以正确的方式使用静态变量。如果它们从不改变,您应该改用常量变量,但您的描述太笼统,无法多说。

      静态成员变量对于你的类的任何实例总是保持相同的值:如果你改变一个对象的静态变量,它也会改变所有其他对象(事实上你也可以在没有实例的情况下访问它们类 - 即:一个对象)。

      【讨论】:

      • 它们现在是 const —— 它们只需要也是静态的,这样我就可以在静态成员函数中使用它们。该规则必须在类内部声明并在类外部定义的原因是什么?这对我来说没有多大意义。
      • @Felix Dombek:我认为原因是(/可以)为您编译和链接的每个源文件声明该类,但实际变量必须只定义一次。这与您需要将其他源文件中定义的变量显式声明为extern 的原因相同。
      • @peoro:这看起来很合理!但是为什么允许整数数据类型呢?那也不应该被允许,那么...
      • @Felix Dombek:这不是标准的抱怨。 ISO C++ 禁止非常量静态成员的类内初始化。您只能对整数 const 静态成员执行此操作,这是因为静态 const 整数变量实际上不会放入内存中,而是在编译时用作常量。
      • @FelixDombek:要回答您关于为什么 C++ 标准不允许这样做的问题,请在此处查看答案:stackoverflow.com/questions/9656941/…
      【解决方案5】:

      有些答案,甚至包括已接受的答案,似乎都有点误导

      你不必

      • 在初始化时始终为静态对象分配一个值,因为这是可选
      • 创建另一个 .cpp 文件进行初始化,因为它可以在同一个头文件中完成

      您甚至可以使用inline 关键字在同一个类范围内初始化一个静态对象,就像一个普通变量一样。


      在同一个文件中没有值初始化

      #include <string>
      class A
      {
          static std::string str;
          static int x;
      };
      std::string A::str;
      int A::x;
      

      使用同一文件中的值进行初始化

      #include <string>
      class A
      {
          static std::string str;
          static int x;
      };
      std::string A::str = "SO!";
      int A::x = 900;
      

      使用inline关键字在同一个类范围内初始化

      #include <string>
      class A
      {
          static inline std::string str = "SO!";
          static inline int x = 900;
      };
      

      【讨论】:

      • 最佳答案。我添加了inline,所有链接错误都消失了。
      • '内联变量是 c++ 17 扩展'。这就是我收到的信息
      • @Ssenyonjo 您必须将 C++ 语言标准设置为 C++17 或更高版本。即在 Visual Studio 中,转到项目 -> 属性 -> C/C++ -> 语言 -> C++ 语言标准 -> ISO C++17 或 ISO C++ 最新。在 GCC 中,使用标志 -std=c++17-std=c++20 等。
      【解决方案6】:

      我觉得值得补充的是,静态变量与常量变量不同。

      在类中使用常量变量

      struct Foo{
          const int a;
          Foo(int b) : a(b){}
      }
      

      我们会像这样声明它

      fooA = new Foo(5);
      fooB = new Foo(10);
      // fooA.a = 5;
      // fooB.a = 10;
      

      对于静态变量

      struct Bar{
          static int a;
          Foo(int b){
              a = b;
          }
      }
      Bar::a = 0; // set value for a
      

      这样用的

      barA = new Bar(5);
      barB = new Bar(10);
      // barA.a = 10;
      // barB.a = 10;
      // Bar::a = 10;
      

      你看这里发生了什么。常量变量与 Foo 的每个实例一起实例化,因为 Foo 被实例化,对于每个 Foo 实例都有一个单独的值,并且它根本不能被 Foo 更改。

      与 Bar 一样,无论创建多少 Bar 实例,它们都只是 Bar::a 的一个值。它们都共享这个值,你也可以通过它们作为 Bar 的任何实例来访问它。静态变量也遵守公共/私有规则,因此您可以使只有 Bar 的实例才能读取 Bar::a 的值;

      【讨论】:

      • 呵呵...觉得很有趣没有人指出我对“新”的可怕不必要的使用。不是很关心这个问题,但是是的,当你不需要时避免使用“新”,你或多或少不会这样做。
      • 请删除您对new 的可怕、不必要的使用。 ;-)
      • 我觉得它现在是一个历史性的例子,说明旧代码有多糟糕。在这一点上,我认为删除它是一种伤害。
      【解决方案7】:

      只是在其他答案之上添加。为了初始化一个复杂的静态成员,你可以这样做:

      像往常一样声明你的静态成员。

      // myClass.h
      class myClass
      {
      static complexClass s_complex;
      //...
      };
      

      创建一个小函数来初始化你的类,如果这样做不是微不足道的。这将仅在静态成员初始化时调用一次。 (注意会用到complexClass的拷贝构造函数,所以要好好定义)。

      //class.cpp    
      #include myClass.h
      complexClass initFunction()
      {
          complexClass c;
          c.add(...);
          c.compute(...);
          c.sort(...);
          // Etc.
          return c;
      }
      
      complexClass myClass::s_complex = initFunction();
      

      【讨论】:

        【解决方案8】:

        如果您的目标是初始化头文件中的静态变量(而不是 *.cpp 文件,如果您坚持使用“仅头文件”习惯用法,则可能需要该文件),那么您可以解决初始化问题通过使用模板。模板化的静态变量可以在标头中初始化,而不会导致定义多个符号。

        查看示例:

        Static member initialization in a class template

        【讨论】:

          【解决方案9】:

          (可选)将所有常量移至 .cpp 文件,而无需在 .h 文件中声明。使用匿名命名空间使它们在 cpp 模块之外不可见。

          // MyClass.cpp
          
          #include "MyClass.h"
          
          // anonymous namespace
          namespace
          {
              string RE_ANY = "([^\\n]*)";
              string RE_ANY_RELUCTANT = "([^\\n]*?)";
          }
          
          // member function (static or not)
          bool MyClass::foo()
          {
              // logic that uses constants
              return RE_ANY_RELUCTANT.size() > 0;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-07-28
            • 1970-01-01
            • 1970-01-01
            • 2012-10-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多