Jas*_*ker 492 c++ initialization static-members
在C++中初始化私有静态数据成员的最佳方法是什么?我在头文件中尝试了这个,但它给了我奇怪的链接器错误:
class foo
{
private:
static int i;
};
int foo::i = 0;
Run Code Online (Sandbox Code Playgroud)
我猜这是因为我无法从课外初始化私人成员.那么最好的方法是什么?
Mar*_*ork 527
类声明应该在头文件中(如果不共享,则在源文件中).
文件:foo.h
class foo
{
private:
static int i;
};
Run Code Online (Sandbox Code Playgroud)
但是初始化应该在源文件中.
文件:foo.cpp
int foo::i = 0;
Run Code Online (Sandbox Code Playgroud)
如果初始化在头文件中,那么包含头文件的每个文件都将具有静态成员的定义.因此,在链接阶段,您将获得链接器错误,因为初始化变量的代码将在多个源文件中定义.
注意:马特柯蒂斯:指出C++允许上述的简化如果静态成员变量是const int的类型(例如static int i,int,bool).然后,您可以直接在头文件中的类声明中声明和初始化成员变量:
class foo
{
private:
static int const i = 42;
};
Run Code Online (Sandbox Code Playgroud)
Mat*_*tis 86
对于变量:
foo.h中:
class foo
{
private:
static int i;
};
Run Code Online (Sandbox Code Playgroud)
Foo.cpp中:
int foo::i = 0;
Run Code Online (Sandbox Code Playgroud)
这是因为程序中只能有一个实例foo::i.它类似于extern int i头文件和int i源文件中的等价物.
对于常量,您可以将值直接放在类声明中:
class foo
{
private:
static int i;
const static int a = 42;
};
Run Code Online (Sandbox Code Playgroud)
Die*_*nte 32
从C++ 17开始,可以在标题中使用inline关键字定义静态成员.
http://en.cppreference.com/w/cpp/language/static
"可以内联声明静态数据成员.可以在类定义中定义内联静态数据成员,并可以指定默认成员初始化程序.它不需要类外定义:"
struct X
{
inline static int n = 1;
};
Run Code Online (Sandbox Code Playgroud)
Jos*_*ton 30
对于这个问题的未来观众,我想指出你应该避免monkey0506的建议.
头文件用于声明.
头文件为每个.cpp直接或间接文件编译一次#includes,并且任何函数之外的代码在程序初始化之前运行main().
通过将:foo::i = VALUE;放入标题,foo:i将为VALUE每个.cpp文件分配值(无论是什么),并且这些分配将在main()运行之前以不确定的顺序(由链接器确定)发生.
如果我们#define VALUE在其中一个.cpp文件中使用不同的数字怎么办?它编译得很好,在我们运行程序之前,我们无法知道哪一个获胜.
永远不要将已执行的代码放入标题中,原因与您从不#include使用.cpp文件相同.
包括警卫(我同意你应该总是使用它)保护你免受不同的事情:#include编译单个.cpp文件时间接d多次相同的标题
Joh*_*ell 19
使用Microsoft编译器[1],非类似的静态变量int也可以在头文件中定义,但在类声明之外,使用Microsoft特定的__declspec(selectany).
class A
{
static B b;
}
__declspec(selectany) A::b;
Run Code Online (Sandbox Code Playgroud)
请注意,我并不是说这很好,我只是说可以做到.
[1]这些天,编译器比MSC支持更多__declspec(selectany)- 至少是gcc和clang.也许更多.
Dav*_*ben 17
int foo::i = 0;
Run Code Online (Sandbox Code Playgroud)
是初始化变量的正确语法,但它必须放在源文件(.cpp)中而不是标题中.
因为它是一个静态变量,所以编译器只需要创建一个副本.你必须有一行"int foo:i"代码中的某些部分告诉编译器将它放在哪里,否则会出现链接错误.如果它在标题中,您将在包含标头的每个文件中获得一个副本,因此从链接器获取多个定义的符号错误.
mon*_*506 12
我没有足够的代表来添加这个作为评论,但IMO用#include警卫编写你的标题是一种很好的风格,正如Paranaix几个小时前所说的那样可以防止出现多重定义错误.除非您已经使用单独的CPP文件,否则不必仅使用一个来初始化静态非整数成员.
#ifndef FOO_H
#define FOO_H
#include "bar.h"
class foo
{
private:
static bar i;
};
bar foo::i = VALUE;
#endif
Run Code Online (Sandbox Code Playgroud)
我认为没有必要为此使用单独的CPP文件.当然,你可以,但没有技术原因你应该这么做.
Kri*_*ski 12
如果你想初始化一些复合类型(fe 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;
}
};
Run Code Online (Sandbox Code Playgroud)
由于它ListInitializationGuard是一个静态变量inform SomeClass::getList()方法,它只会被构造一次,这意味着构造函数被调用一次.这将initialize _list根据您的需要变化.任何后续调用都getList将只返回已初始化的_list对象.
当然,您必须_list始终通过调用getList()方法来访问对象.
您遇到的链接器问题可能是由以下原因引起的:
对于刚接触 C++ 的人来说,这是一个常见问题。静态类成员必须在单个翻译单元(即单个源文件)中初始化。
不幸的是,静态类成员必须在类体之外初始化。这使得编写仅包含标头的代码变得复杂,因此,我使用了完全不同的方法。您可以通过静态或非静态类函数提供静态对象,例如:
class Foo
{
// int& getObjectInstance() const {
static int& getObjectInstance() {
static int object;
return object;
}
void func() {
int &object = getValueInstance();
object += 5;
}
};
Run Code Online (Sandbox Code Playgroud)
适用于多个对象的C ++ 11静态构造函数模式
提出了一个惯用法,网址为:https : //stackoverflow.com/a/27088552/895245,但是此处提供了一种更干净的版本,不需要为每个成员创建新方法。
main.cpp
#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);
}
Run Code Online (Sandbox Code Playgroud)
编译并运行:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out
Run Code Online (Sandbox Code Playgroud)
另请参见:C ++中的静态构造函数?我需要初始化私有静态对象
在Ubuntu 19.04上测试。
C ++ 17内联变量
在以下位置提及:https : //stackoverflow.com/a/45062055/895245,但这是一个可运行的多文件示例,以使其更清晰:内联变量如何工作?
小智 5
如果使用标题保护,还可以在头文件中包含赋值.我已经将这种技术用于我创建的C++库.实现相同结果的另一种方法是使用静态方法.例如...
class Foo
{
public:
int GetMyStatic() const
{
return *MyStatic();
}
private:
static int* MyStatic()
{
static int mStatic = 0;
return &mStatic;
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码具有不需要CPP /源文件的"奖励".同样,我用于我的C++库的方法.
| 归档时间: |
|
| 查看次数: |
474146 次 |
| 最近记录: |