当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在C++中初始化私有静态成员?》经验,为你挑选了10个好方法。

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

class foo
{
    private:
        static int i;
};

int foo::i = 0;

我猜这是因为我无法从课外初始化私人成员.那么最好的方法是什么?



1> Martin York..:

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

class foo
{
    private:
        static int i;
};

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

int foo::i = 0;

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

注意:马特柯蒂斯:指出C++允许上述的简化如果静态成员变量是const int的类型(例如static int i,int,bool).然后,您可以直接在头文件中的类声明中声明和初始化成员变量:

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


实际上不仅仅是POD,它也必须是一个int类型(int,short,bool,char ......)
请注意,这不仅仅是如何初始化值的问题:这样定义的const整数类型可能会被实现转换为编译时常量.这并不总是你想要的,因为它提升了二进制依赖:如果值改变,客户端代码需要重新编译.
是.但我假设问题已经简化了.从技术上讲,声明和定义都可以在一个源文件中.但那限制了其他类的使用.
@Martin:除了纠正s/POD /积分类型/之外,如果地址被采用,那么还需要有一个定义.听起来很奇怪,在类定义中,初始化器的声明不是定义.*模板化常用语*为您需要头文件中的定义的情况提供了一种解决方法.另一种更简单的解决方法是生成局部静态常量值的函数.干杯&hth.,
您可以添加一个说明int foo :: i = 0; 不应该在函数内部(包括main函数).我在主要功能的开头有它,它不喜欢它.
@MattCurtis"或任何类型,如果你使用的是C++ 11".虽然值得添加,但我理解如果另外指定为非整数类型的constexpr.

2> Matt Curtis..:

对于变量:

foo.h中:

class foo
{
private:
    static int i;
};

Foo.cpp中:

int foo::i = 0;

这是因为程序中只能有一个实例foo::i.它类似于extern int i头文件和int i源文件中的等价物.

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

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


这是一个有效的观点.我也会补充说明这一点.但应该指出的是,这仅适用于POD类型.

3> Die in Sente..:

从C++ 17开始,可以在标题中使用inline关键字定义静态成员.

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

"可以内联声明静态数据成员.可以在类定义中定义内联静态数据成员,并可以指定默认成员初始化程序.它不需要类外定义:"

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



4> Joshua Clayt..:

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

头文件用于声明.

头文件为每个.cpp直接或间接文件编译一次#includes,并且任何函数之外的代码在程序初始化之前运行main().

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

如果我们#define VALUE在其中一个.cpp文件中使用不同的数字怎么办?它编译得很好,在我们运行程序之前,我们无法知道哪一个获胜.

永远不要将已执行的代码放入标题中,原因与您从不#include使用.cpp文件相同.

包括警卫(我同意你应该总是使用它)保护你免受不同的事情:#include编译单个.cpp文件时间接d多次相同的标题


你当然是对的,除了类模板的情况(没有被问及,但我碰巧经常处理).因此,如果类是完全定义的而不是类模板,则将这些静态成员放在单独的CPP文件中,但对于类模板,定义必须位于相同的转换单元(例如,头文件)中.

5> Johann Gerel..:

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

class A
{
    static B b;
}

__declspec(selectany) A::b;

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

[1]这些天,编译器比MSC支持更多__declspec(selectany)- 至少是gcc和clang.也许更多.



6> David Dibben..:
int foo::i = 0; 

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

因为它是一个静态变量,所以编译器只需要创建一个副本.你必须有一行"int foo:i"代码中的某些部分告诉编译器将它放在哪里,否则会出现链接错误.如果它在标题中,您将在包含标头的每个文件中获得一个副本,因此从链接器获取多个定义的符号错误.



7> monkey0506..:

我没有足够的代表来添加这个作为评论,但IMO用#include警卫编写你的标题是一种很好的风格,正如Paranaix几个小时前所说的那样可以防止出现多重定义错误.除非您已经使用单独的CPP文件,否则不必仅使用一个来初始化静态非整数成员.

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

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

我认为没有必要为此使用单独的CPP文件.当然,你可以,但没有技术原因你应该这么做.


#include guards只是防止每个翻译单元有多个定义.
这只适用于只有一个包含foo.h的编译单元的情况.如果两个或多个cpps包含foo.h,这是典型情况,每个cpp都会声明相同的静态变量,因此链接器会抱怨`foo :: i'的多个定义,除非你使用包编译文件(编译)只有一个包含所有cpps的文件.但是虽然包编译很棒,但问题的解决方案是在cpp中声明(int foo :: i = 0;)!
关于好的风格:你应该在结束时加上评论:`#endif // FOO_H`

8> 小智..:

如果你想初始化一些复合类型(fe string)你可以做类似的事情:

class SomeClass {
  static std::list _list;

  public:
    static const std::list& 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是一个静态变量inform SomeClass::getList()方法,它只会被构造一次,这意味着构造函数被调用一次.这将initialize _list根据您的需要变化.任何后续调用都getList将只返回已初始化的_list对象.

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



9> Ciro Santill..:

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

提出了一个惯用法,网址为:https : //stackoverflow.com/a/27088552/895245,但是此处提供了一种更干净的版本,不需要为每个成员创建新方法。

main.cpp

#include 
#include 

// Normally on the .hpp file.
class MyClass {
public:
    static std::vector 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 MyClass::v;
std::vector 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上游。

编译并运行:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

另请参见:C ++中的静态构造函数?我需要初始化私有静态对象

在Ubuntu 19.04上测试。

C ++ 17内联变量

在以下位置提及:https : //stackoverflow.com/a/45062055/895245,但这是一个可运行的多文件示例,以使其更清晰:内联变量如何工作?



10> 小智..:

如果使用标题保护,还可以在头文件中包含赋值.我已经将这种技术用于我创建的C++库.实现相同结果的另一种方法是使用静态方法.例如...

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

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

上面的代码具有不需要CPP /源文件的"奖励".同样,我用于我的C++库的方法.

推荐阅读
k78283381
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有