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

问题描述 投票:0回答:1

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

class foo
{
    private:
        static int i;
};

int foo::i = 0;

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

c++ static unordered-map
1个回答
527
投票

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

class foo
{
    private:
        static int i;
};

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

int foo::i = 0;

如果初始化在头文件中,则每个包含头文件的文件都将具有静态成员的定义。因此,在链接阶段,您将得到链接器错误,因为初始化变量的代码将在多个源文件中定义。static int i的初始化必须在任何功能之外进行。

注: Matt Curtis:指出,如果静态成员变量为const int类型(例如intboolchar),则C ++可以简化上述操作。然后,您可以直接在头文件的类声明中声明和初始化成员变量:

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

5
投票

如果您使用标题保护,您也可以在头文件中包含分配。我将这种技术用于我创建的C ++库。获得相同结果的另一种方法是使用静态方法。例如...

How do inline variables work?

上面的代码具有不需要CPP /源文件的“好处”。同样,这是我用于C ++库的一种方法。


4
投票

我遵循卡尔的想法。我喜欢它,现在也使用它。我已经更改了一点符号并添加了一些功能

class Foo
   {
   public:
     int GetMyStatic() const
     {
       return *MyStatic();
     }

   private:
     static int* MyStatic()
     {
       static int mStatic = 0;
       return &mStatic;
     }
   }

此输出

#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));
}

3
投票

也正在privateStatic.cpp文件中工作:

mystatic value 7
mystatic value 3
is my static 1 0

3
投票

#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 方法怎么样?

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)变量。

这不会与其余评论意见相抵触,实际上,它遵循在全局范围内初始化变量的相同原理,但是通过使用此方法,我们使它变得明确(并且易于理解),而不是具有悬在那里的变量的定义。


3
投票

您遇到的链接器问题可能是由于:

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

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

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

static

2
投票

一种定义常量的“老派”方法是将其替换为class Foo { // int& getObjectInstance() const { static int& getObjectInstance() { static int object; return object; } void func() { int &object = getValueInstance(); object += 5; } };

enum

这种方法不需要提供定义,并且避免了设置常数class foo { private: enum {i = 0}; // default type = int enum: int64_t {HUGE = 1000000000000}; // may specify another type }; ,这样可以为您省去一些麻烦,例如当您不小心lvalue时。


1
投票

当我第一次遇到这个问题时,我只是想提一些对我来说有些奇怪的东西。

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

在.h或.hpp中,看起来像这样来初始化模板类的静态数据成员:

ODR-use

0
投票

这符合您的目的吗?

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;

86
投票

对于变量

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

这是因为程序中只有foo::i的一个实例。有点像头文件中的extern int i和源文件中的int i

对于constant,您可以将值直接放在类声明中:

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

32
投票

自C ++ 17起,可以在标题中使用inline关键字定义静态成员。

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

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

struct X
{
    inline static int n = 1;
};

30
投票

对于这个问题的将来的观看者,我想指出您应该避免使用monkey0506 is suggesting

头文件用于声明。

头文件针对直接或间接.cpp它们的每个#includes文件进行一次编译,并且在main()之前,在程序初始化时运行任何功能之外的代码。

[通过:将foo::i = VALUE;放入标题,将为每个foo:i文件分配VALUE值[无论是什么],并且这些分配将以不确定的顺序发生(由链接器确定)在运行.cpp之前。

如果我们在一个main()文件中将#define VALUE设置为另一个数字该怎么办?它可以正常编译,在运行该程序之前,我们将无法知道哪一个获胜。

出于与从未.cpp一个#include文件相同的原因,切勿将执行的代码放入标头中。

包括守护程序(我同意您应该一直使用)保护您免受不同的攻击:同一头文件在编译单个.cpp文件时被间接#include d多次复制


19
投票

[使用Microsoft编译器[1],也可以使用Microsoft特定的.cpp在头文件中,但在类声明的外部,定义不类似于int的静态变量。

__declspec(selectany)

请注意,我并不是说这很好,我只是说可以做到。

[1]如今,比MSC支持的编译器多class A { static B b; } __declspec(selectany) A::b; -至少是gcc和clang。甚至更多。


17
投票
__declspec(selectany)

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

因为它是一个静态变量,所以编译器只需要创建一个副本。您必须在代码中的某些位置插入“ int foo:i”行,以告诉编译器将其放置在何处,否则会出现链接错误。如果在标头中,则将在每个包含标头的文件中获得一个副本,因此请从链接器获取多个已定义的符号错误。


12
投票

我在这里没有足够的代表将该注释添加为注释,但是IMO仍然是使用int foo::i = 0; 编写标题的好方法,正如Paranaix几个小时前指出的那样,可以防止出现多定义错误。除非您已经在使用单独的CPP文件,否则不必仅使用一个文件来初始化静态非积分成员。

#include guards

我认为不需要为此使用单独的CPP文件。当然可以,但是没有技术理由必须这样做。


12
投票

如果要初始化某种复合类型(例如字符串),可以执行类似的操作:

#ifndef FOO_H
#define FOO_H
#include "bar.h"

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

由于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方法来访问_list对象。


6
投票

可用于多个对象的C ++ 11静态构造函数模式

getList()提出了一个习惯用法,但是这里有一个更干净的版本,不需要为每个成员创建新方法。

main.cpp

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

另请参见:g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp ./main.out

在Ubuntu 19.04上测试。

C ++ 17内联变量

提及位置:static constructors in C++? I need to initialize private static objects,但这是一个可运行的多文件示例,使它更清晰:https://stackoverflow.com/a/45062055/895245

热门问题
推荐问题
最新问题