在c++中初始化私有静态数据成员的最佳方法是什么?我在头文件中尝试了这一点,但它给了我奇怪的链接器错误:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

我猜这是因为我不能从类外部初始化一个私有成员。那么最好的方法是什么呢?


当前回答

int foo::i = 0; 

是初始化变量的正确语法,但它必须放在源文件(.cpp)中,而不是放在头文件中。

因为它是一个静态变量,所以编译器只需要创建它的一个副本。你必须在你的代码中有一行"int foo:i"来告诉编译器把它放在哪里,否则你会得到一个链接错误。如果这是在一个头,你会得到一个拷贝在每个文件,包括头,所以从链接器获得多重定义的符号错误。

其他回答

对于这个问题的未来观众,我想指出您应该避免monkey0506所建议的内容。

头文件用于声明。

对于每个直接或间接包含头文件的.cpp文件,头文件将被编译一次,并且在main()之前在程序初始化时运行任何函数之外的代码。

通过输入:foo::i = VALUE;对于每个.cpp文件,foo:i将被赋值value(不管它是什么),并且这些赋值将以不确定的顺序(由链接器决定)在main()运行之前发生。

如果我们在其中一个。cpp文件中#define VALUE为不同的数字会怎样?它将编译良好,我们将无法知道哪个胜出,直到我们运行程序。

永远不要将执行的代码放入头文件中,这与您永远不要#include .cpp文件的原因相同。

Include守卫(我同意你应该经常使用)保护你不受一些不同情况的影响:在编译一个.cpp文件时,同一个头文件被间接地多次# Include。

类声明应该在头文件中(如果不共享,则在源文件中)。 文件:foo。

class foo
{
    private:
        static int i;
};

但是初始化应该在源文件中。 文件:foo.cpp

int foo::i = 0;

如果初始化是在头文件中,那么每个包含头文件的文件都有一个静态成员的定义。因此,在链接阶段,你会得到链接器错误,因为初始化变量的代码将在多个源文件中定义。 静态int i的初始化必须在任何函数之外完成。

注意:Matt Curtis:指出,如果静态成员变量是const整数类型(bool, char, char8_t[自c++ 20以来],char16_t, char32_t, wchar_t, short, int, long, long long,或任何实现定义的扩展整数类型,包括任何有符号,无符号和cv限定变量),c++允许简化上述内容。然后你可以直接在头文件的类声明中声明和初始化成员变量:

class foo
{
    private:
        static int const i = 42;
};

如果你想初始化一些复合类型(f.e. string),你可以这样做:

class SomeClass {
  static std::list<string> _list;

  public:
    static const std::list<string>& getList() {
      struct Initializer {
         Initializer() {
           // Here you may want to put mutex
           _list.push_back("FIRST");
           _list.push_back("SECOND");
           ....
         }
      }
      static Initializer ListInitializationGuard;
      return _list;
    }
};

由于ListInitializationGuard是SomeClass::getList()方法中的一个静态变量,它将只被构造一次,这意味着构造函数被调用一次。这将初始化_list变量为你需要的值。任何后续对getList的调用都将返回已经初始化的_list对象。

当然,您必须始终通过调用getList()方法访问_list对象。

我遵循卡尔的想法。我喜欢它,现在我也在用它。 我稍微改变了一下符号,增加了一些功能

#include <stdio.h>

class Foo
{
   public:

     int   GetMyStaticValue () const {  return MyStatic();  }
     int & GetMyStaticVar ()         {  return MyStatic();  }
     static bool isMyStatic (int & num) {  return & num == & MyStatic(); }

   private:

      static int & MyStatic ()
      {
         static int mStatic = 7;
         return mStatic;
      }
};

int main (int, char **)
{
   Foo obj;

   printf ("mystatic value %d\n", obj.GetMyStaticValue());
   obj.GetMyStaticVar () = 3;
   printf ("mystatic value %d\n", obj.GetMyStaticValue());

   int valMyS = obj.GetMyStaticVar ();
   int & iPtr1 = obj.GetMyStaticVar ();
   int & iPtr2 = valMyS;

   printf ("is my static %d %d\n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2));
}

这个输出

mystatic value 7
mystatic value 3
is my static 1 0

以下是一个简单例子中的所有可能性和错误……

#ifndef Foo_h
#define Foo_h

class Foo
{
  static const int a = 42; // OK
  static const int b {7};  // OK
  //static int x = 42; // ISO C++ forbids in-class initialization of non-const static member 'Foo::x'
  //static int y {7};  // ISO C++ forbids in-class initialization of non-const static member 'Foo::x'
  static int x;
  static int y;
  int m = 42;
  int n {7};
};

// Foo::x = 42;  // error: 'int Foo::x' is private
int Foo::x = 42; // OK in Foo.h if included in only one  *.cpp -> *.o file!
int Foo::y {7};  // OK

// int Foo::y {7};  // error: redefinition of 'int Foo::y'
   // ONLY if the compiler can see both declarations at the same time it, 
   // OTHERWISE you get a linker error

#endif // Foo_h

但最好把它放在Foo.cpp中。这样你就可以单独编译每个文件并在以后链接它们,否则Foo:x将出现在多个目标文件中,并导致链接器错误. ...

// Foo::x = 42;  // error: 'int Foo::x' is private, bad if Foo::X is public!
int Foo::x = 42; // OK in Foo.h if included in only one  *.cpp -> *.o file!
int Foo::y {7};  // OK