代码之家  ›  专栏  ›  技术社区  ›  Jason Baker

如何在C++中初始化私有静态成员?

  •  447
  • Jason Baker  · 技术社区  · 16 年前

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

    class foo
    {
        private:
            static int i;
    };
    
    int foo::i = 0;
    

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

    17 回复  |  直到 6 年前
        1
  •  484
  •   Loki Astari    9 年前

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

    class foo
    {
        private:
            static int i;
    };
    

    但是初始化应该在源文件中。
    文件:英尺.CPP

    int foo::i = 0;
    

    如果初始化在头文件中,则包含头文件的每个文件都将具有静态成员的定义。因此,在链接阶段,您将得到链接器错误,因为初始化变量的代码将在多个源文件中定义。

    注: Matt Curtis:指出,如果静态成员变量是const int类型,那么C++就可以简化上面的(例如)。 int , bool , char )然后可以直接在头文件中的类声明内声明和初始化成员变量:

    class foo
    {
        private:
            static int const i = 42;
    };
    
        2
  •  81
  •   Matt Curtis    16 年前

    对于一个 变量 :

    福:

    class foo
    {
    private:
        static int i;
    };
    

    英尺:CPP:

    int foo::i = 0;
    

    这是因为只有一个 foo::i 在你的程序中。它有点像 extern int i 在头文件中, int i 在源文件中。

    对于一个 常数 您可以在类声明中直接输入值:

    class foo
    {
    private:
        static int i;
        const static int a = 42;
    };
    
        3
  •  25
  •   Community leo1    7 年前

    对于这个问题的未来观众,我想指出的是,你应该避免 monkey0506 is suggesting .

    头文件用于声明。

    头文件每编译一次 .cpp 直接或间接提交 #includes 它们和任何函数外部的代码在程序初始化时运行,在 main() .

    通过: foo::i = VALUE; 进入标题, foo:i 将被分配该值 VALUE (不管那是什么)为了 CPP 文件,这些分配将以不确定的顺序(由链接器确定)发生 主体() 运行。

    如果我们 #define VALUE 在我们的 CPP 文件夹?它编译得很好,在运行程序之前,我们无法知道谁会赢。

    永远不要将执行的代码放入头中,原因与 #include CPP 文件。

    包括防护装置(我同意你应该经常使用)保护你不受其他东西的伤害:同一个头部是间接的 包括: 在编译一个 CPP 文件

        4
  •  19
  •   Johann Gerell    9 年前

    使用Microsoft编译器[1]时,静态变量不是 int -like也可以在头文件中定义,但在类声明之外,使用特定于Microsoft的 __declspec(selectany) .

    class A
    {
        static B b;
    }
    
    __declspec(selectany) A::b;
    

    注意,我不是说这是好的,我只是说这是可以做到的。

    [1]现在,编译器比MSC支持的多 _ declspec(选择任意) -至少GCC和Clang。也许更多。

        5
  •  16
  •   David Dibben    16 年前
    int foo::i = 0; 
    

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

    因为它是一个静态变量,所以编译器只需要创建它的一个副本。你必须有一行“int foo:i”来告诉编译器把它放在哪里,否则你会得到一个链接错误。如果它在一个头文件中,那么您将在包含该头文件的每个文件中获得一个副本,因此从链接器中获取多个定义的符号错误。

        6
  •  16
  •   Xunie Die in Sente    7 年前

    由于C++ 17,静态成员可以在头文件中定义 内联的 关键字。

    http://en.cppreference.com/w/cpp/language/static

    “静态数据成员可以内联声明。可以在类定义中定义内联静态数据成员,并且可以指定默认成员初始值设定项。它不需要类外定义:“

    struct X
    {
        inline static int n = 1;
    };
    
        7
  •  11
  •   monkey0506    13 年前

    我这里没有足够的代表来添加这个评论,但是我觉得用它来写你的标题是一种很好的风格。 #include guards 无论如何,正如几个小时前paraix指出的那样,这将防止多定义错误。除非您已经在使用一个单独的cpp文件,否则不必只使用一个文件来初始化静态非整型成员。

    #ifndef FOO_H
    #define FOO_H
    #include "bar.h"
    
    class foo
    {
    private:
        static bar i;
    };
    
    bar foo::i = VALUE;
    #endif
    

    我认为没有必要为此使用单独的cpp文件。当然,你可以,但是没有技术上的原因让你不得不这么做。

        8
  •  10
  •   Ziezi sethi    9 年前

    如果要初始化某个复合类型(F.E.字符串),可以这样做:

    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() 方法将只构造一次,这意味着构造函数被调用一次。本遗嘱 initialize _list 变量到您需要的值。任何后续呼叫 getList 只返回已初始化的 _list 对象。

    当然你得去 列表 对象始终通过调用 getList() 方法。

        9
  •  5
  •   user2225284    11 年前

    如果使用头保护,也可以在头文件中包含分配。我已经用这个技术创建了一个C++库。实现相同结果的另一种方法是使用静态方法。例如。。。

    class Foo
       {
       public:
         int GetMyStatic() const
         {
           return *MyStatic();
         }
    
       private:
         static int* MyStatic()
         {
           static int mStatic = 0;
           return &mStatic;
         }
       }
    

    上述代码的“好处”是不需要cpp/源文件。再次,我使用的方法用于我的C++库。

        10
  •  4
  •   Alejadro Xalabarder    11 年前

    我听从卡尔的意见。我喜欢它,现在我也用它。 我改变了一点符号,增加了一些功能。

    #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
    
        11
  •  4
  •   Ciro Santilli OurBigBook.com    6 年前

    适用于多个对象的静态构造函数模式

    一个成语是在: https://stackoverflow.com/a/27088552/895245 但这里有一个更清晰的版本,它不需要为每个成员创建一个新的方法,以及一个可运行的示例:

    #include <cassert>
    #include <vector>
    
    // Normally on the .hpp file.
    class MyClass {
    public:
        static std::vector<int> v, v2;
        static struct _StaticConstructor {
            _StaticConstructor() {
                v.push_back(1);
                v.push_back(2);
                v2.push_back(3);
                v2.push_back(4);
            }
        } _staticConstructor;
    };
    
    // Normally on the .cpp file.
    std::vector<int> MyClass::v;
    std::vector<int> MyClass::v2;
    // Must come after every static member.
    MyClass::_StaticConstructor MyClass::_staticConstructor;
    
    int main() {
        assert(MyClass::v[0] == 1);
        assert(MyClass::v[1] == 2);
        assert(MyClass::v2[0] == 3);
        assert(MyClass::v2[1] == 4);
    }
    

    GitHub upstream .

    参见: static constructors in C++? I need to initialize private static objects

    用试验 g++ -std=c++11 -Wall -Wextra 通用条款第7.3款,Ubuntu 18.04。

        12
  •  3
  •   andrew    11 年前

    同时在privatestatic.cpp文件中工作:

    #include <iostream>
    
    using namespace std;
    
    class A
    {
    private:
      static int v;
    };
    
    int A::v = 10; // possible initializing
    
    int main()
    {
    A a;
    //cout << A::v << endl; // no access because of private scope
    return 0;
    }
    
    // g++ privateStatic.cpp -o privateStatic && ./privateStatic
    
        13
  •  3
  •   Ziezi sethi    9 年前

    A怎么样 set_default() 方法?

    class foo
    {
        public:
            static void set_default(int);
        private:
            static int i;
    };
    
    void foo::set_default(int x) {
        i = x;
    }
    

    我们只需要使用 set_default(int x) 方法与我们 static 变量将被初始化。

    这与注释的其他部分没有什么不同,实际上它遵循了在全局范围内初始化变量的相同原则,但是通过使用此方法,我们使变量显式(并且易于理解)而不是将变量的定义挂在那里。

        14
  •  2
  •   no one special    7 年前

    您遇到的链接器问题可能是由以下原因引起的:

    • 在头文件中同时提供类和静态成员定义,
    • 将此头文件包含在两个或多个源文件中。

    对于那些从C++开始的人来说,这是一个常见的问题。静态类成员必须在单个翻译单元中初始化,即在单个源文件中初始化。

    不幸的是,静态类成员必须在类体外部初始化。这使得只写头代码变得复杂,因此,我使用了完全不同的方法。可以通过静态或非静态类函数提供静态对象,例如:

    class Foo
    {
        // int& getObjectInstance() const {
        static int& getObjectInstance() {
            static int object;
            return object;
        }
    
        void func() {
            int &object = getValueInstance();
            object += 5;
        }
    };
    
        15
  •  1
  •   Tyler Heers    8 年前

    当我第一次遇到这个的时候,我只想提一件有点奇怪的事情。

    我需要初始化模板类中的私有静态数据成员。

    在.h或.hpp中,初始化模板类的静态数据成员如下所示:

    template<typename T>
    Type ClassName<T>::dataMemberName = initialValue;
    
        16
  •  1
  •   anatolyg    6 年前

    定义常量的一种“老派”方法是用 enum :

    class foo
    {
        private:
            enum {i = 0}; // default type = int
            enum: int64_t {HUGE = 1000000000000}; // may specify another type
    };
    

    这种方法不需要提供定义,并且避免了 lvalue 这可以帮你减轻一些头疼,例如当你不小心 ODR-use 它。

        17
  •  0
  •   corporateAbaper    8 年前

    这符合你的目的吗?

    //header file
    
    struct MyStruct {
    public:
        const std::unordered_map<std::string, uint32_t> str_to_int{
            { "a", 1 },
            { "b", 2 },
            ...
            { "z", 26 }
        };
        const std::unordered_map<int , std::string> int_to_str{
            { 1, "a" },
            { 2, "b" },
            ...
            { 26, "z" }
        };
        std::string some_string = "justanotherstring";  
        uint32_t some_int = 42;
    
        static MyStruct & Singleton() {
            static MyStruct instance;
            return instance;
        }
    private:
        MyStruct() {};
    };
    
    //Usage in cpp file
    int main(){
        std::cout<<MyStruct::Singleton().some_string<<std::endl;
        std::cout<<MyStruct::Singleton().some_int<<std::endl;
        return 0;
    }