与静态构造函数类似,如何初始化静态数据成员?
我想要一个带有私有静态数据成员的类:
class C {
// read-only, can also be static const
// should be filled with all characters from 'a' to 'z'
static std::vector<char> alphabet;
public:
C() { /* ... */ }
};
在 Java 或 C# 中,我可以创建一个“静态构造函数”,该构造函数将在创建该类的任何实例之前运行,并设置该类的静态数据成员。 它只运行一次(因为变量是只读的,并且只需要设置一次),并且由于它是类的函数,因此它可以访问其私有成员。
我可以在 C()
构造函数中添加代码来检查向量是否已初始化,如果未初始化则对其进行初始化,但这引入了许多必要的检查,并且似乎不是最佳解决方案问题。
该成员是只读的,因此它可以是static const
,并且我可以在类之外定义它,但这似乎是一个丑陋的黑客行为。
如果我不想在实例构造函数中初始化它们,是否可以在类中拥有私有静态数据成员?
I want to have a class with a private static data member:
class C {
// read-only, can also be static const
// should be filled with all characters from 'a' to 'z'
static std::vector<char> alphabet;
public:
C() { /* ... */ }
};
In Java or C#, I can just make a "static constructor" that will run before I make any instances of the class, and sets up the static data members of the class. It only gets run once (as the variables are read only and only need to be set once) and since it's a function of the class it can access its private members.
I could add code in the C()
constructor that checks to see if the vector is initialized, and initialize it if it's not, but that introduces many necessary checks and doesn't seem like the optimal solution to the problem.
The member is read-only, so it can be static const
and I can define it outside the class, but that seems like an ugly hack.
Is it possible to have private static data members in a class if I don't want to initialize them in the instance constructor?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
要初始化静态变量,只需在源文件内执行此操作即可。 例如:
To initialize a static variable, you just do so inside of a source file. For example:
创建一个模板来模仿 C# 的行为怎么样?
How about creating a template to mimic the behavior of C#.
对于像这里这样的简单情况,包装在静态成员函数内的静态变量几乎同样好。 它很简单,通常会被编译器优化掉。 但这并不能解决复杂对象的初始化顺序问题。
For simple cases like here a static variable wrapped inside a static member function is nearly as good. It's simple and will usually be optimized away by compilers. This does not solve initialization order problem for complex objects though.
这是一个解决方案吗?
Is this a solution?
可以使用友元类或嵌套类来模拟静态构造函数,如下所示。
输出:
A static constructor can be emulated by using a friend class or nested class as below.
Output:
我使用这种技术来初始化静态内容:
在.cpp文件中:
因为它在编译单元中,所以变量initMe将具有程序生命周期。 构造函数将在 main() 之前初始化,析构函数(如果有)将在程序终止之前调用。
与所有静态内容一样,无法保证其执行顺序,但在单个 .cpp 文件中,可以保证静态内容将按照它们在文件中出现的顺序进行初始化。
I use this technique to init static stuff :
In the .cpp file :
Because it's in a compilation unit, the variable initMe will have a program lifetime. The constructor will be initialized before main(), and the destructor, if any, will be called before program termination.
Like all static stuff, there's no guarantee on the order it will be executed, but in a single .cpp file, there's a guarantee that static stuff will be initialized in order they appear in the file.
这是另一种方法,其中向量对于包含使用匿名命名空间的实现的文件来说是私有的。 它对于诸如实现私有的查找表之类的东西很有用:
Here's another method, where the vector is private to the file that contains the implementation by using an anonymous namespace. It's useful for things like lookup tables that are private to the implementation:
它当然不需要像当前接受的答案(丹尼尔·厄威克)那么复杂。 班级是多余的。 在这种情况下,没有必要进行语言战争。
.hpp 文件:
.cpp 文件:
It certainly doesn't need to be as complicated as the currently accepted answer (by Daniel Earwicker). The class is superfluous. There's no need for a language war in this case.
.hpp file:
.cpp file:
GCC 提供
https://gcc.gnu.org/ onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
使用此属性标记静态方法,它将在 main() 之前的模块加载时运行。
GCC offers
https://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Function-Attributes.html
Tag a static method with this attribute and it will run on module load, before main().
定义静态成员变量的方式与定义成员方法的方式类似。
foo.h
foo.cpp
You define static member variables similarly to the way you define member methods.
foo.h
foo.cpp
我想这个问题的简单解决方案是:
I guess Simple solution to this will be:
哇,我不敢相信没有人提到最明显的答案,以及最接近 C# 静态构造函数行为的答案,即在创建该类型的第一个对象之前不会调用它。
std::call_once()
在 C++11 中可用; 如果您不能使用它,可以使用静态布尔类变量和比较和交换原子操作来完成。 在构造函数中,查看是否可以自动将类静态标志从false
更改为true
,如果可以,则可以运行静态构造代码。为了获得额外的分数,请将其设置为 3 路标志而不是布尔值,即未运行、正在运行和完成运行。 然后该类的所有其他实例都可以自旋锁,直到运行静态构造函数的实例完成(即发出内存栅栏,然后将状态设置为“完成运行”)。 你的自旋锁应该执行处理器的“暂停”指令,每次等待时间加倍,直到达到阈值,等等——相当标准的自旋锁技术。
在没有 C++11 的情况下, 这个 应该得到你开始了。
这里有一些伪代码来指导您。 将其放入您的类定义中:
将其放入您的构造函数中:
Wow, I can't believe no one mentioned the most obvious answer, and one that most closely mimics C#'s static-constructor behavior, i.e. it doesn't get called until the first object of that type is created.
std::call_once()
is available in C++11; if you can't use that, it can be done with a static boolean class-variable, and a compare-and-exchange atomic-operation. In your constructor, see if you can atomically change the class-static flag fromfalse
totrue
, and if so, you can run the static-construction code.For extra credit, make it a 3-way flag instead of a boolean, i.e. not run, running, and done running. Then all other instances of that class can spin-lock until the instance running the static-constructor has finished (i.e. issue a memory-fence, then set the state to "done running"). Your spin-lock should execute the processor's "pause" instruction, double the wait each time up until a threshold, etc. — pretty standard spin-locking technique.
In the absence of C++11, this should get you started.
Here's some pseudocode to guide you. Put this in your class definition:
And this in your constructor:
刚刚解决了同样的技巧。 我必须为 Singleton 指定单个静态成员的定义。
但让事情变得更复杂 - 我决定我不想调用 RandClass() 的 ctor 除非我要使用它......这就是为什么我不想在我的代码中全局初始化单例。 我还在我的案例中添加了简单的界面。
这是最终的代码:
我简化了代码并使用 rand() 函数及其单种子初始值设定项 srand()
Just solved same trick. I had to specify definition of a single static member for Singleton.
But make things more complicated - I have decided that I do not want to call ctor of RandClass() unless I am gonna use it... that is why I did not want to initialize singleton globally in my code. Also I've added simple interface in my case.
Here is the final code:
I simplified code and use rand() function and its single seed initialzer srand()
这是我的 EFraim 解决方案的变体; 区别在于,由于隐式模板实例化,只有创建类的实例时才会调用静态构造函数,并且不需要在 .cpp 文件中进行定义(感谢模板实例化魔法) 。
在
.h
文件中,您可以:在
.cpp
文件中,您可以:请注意,仅当满足以下条件时,
MyClass::a
才会被初始化:第 [1] 行在那里,因为它调用(并需要实例化)构造函数,然后需要实例化_initializer
。Here's my variant of EFraim's solution; the difference is that, thanks to implicit template instantiation, the static constructor is only called if instances of the class are created, and that no definition in the
.cpp
file is needed (thanks to template instantiation magic).In the
.h
file, you have:In the
.cpp
file, you can have:Note that
MyClass::a
is initialized only if line [1] is there, because that calls (and requires instantiation of) the constructor, which then requires instantiation of_initializer
.Java 吸取了 C++ 的问题后,引入了静态构造函数的概念。 所以我们没有直接的等价物。
最好的解决方案是使用可以显式初始化的 POD 类型。
或者使您的静态成员成为具有自己的构造函数的特定类型,该构造函数可以正确初始化它。
The concept of static constructors was introduced in Java after they learned from the problems in C++. So we have no direct equivalent.
The best solution is to use POD types that can be initialised explicitly.
Or make your static members a specific type that has its own constructor that will initialize it correctly.
当尝试编译和使用类
Elsewhere
时(来自Earwicker 的回答)我得到:似乎不可能在不将一些代码放在类定义之外的情况下初始化非整数类型的静态属性(CPP )。
要进行编译,您可以使用“内部带有静态局部变量的静态方法”。 像这样的事情:
你还可以将参数传递给构造函数或用特定值初始化它,它非常灵活,强大且易于实现......唯一的事情是你有一个包含静态变量的静态方法,而不是静态属性...语法略有变化,但仍然有用。 希望这对雨果·冈萨雷斯·卡斯特罗(Hugo González Castro)有用
。
When trying to compile and use class
Elsewhere
(from Earwicker's answer) I get:It seems is not possible to initialize static attributes of non-integer types without putting some code outside the class definition (CPP).
To make that compile you can use "a static method with a static local variable inside" instead. Something like this:
And you may also pass arguments to the constructor or initialize it with specific values, it is very flexible, powerfull and easy to implement... the only thing is you have a static method containing a static variable, not a static attribute... syntaxis changes a bit, but still useful. Hope this is useful for someone,
Hugo González Castro.
Test::StaticTest()
在全局静态初始化期间仅调用一次。调用者只需向函数添加一行即可作为其静态构造函数。
static_constructor<&Test::StaticTest>::c;
在全局静态初始化期间强制初始化c
。Test::StaticTest()
is called exactly once during global static initialization.Caller only has to add one line to the function that is to be their static constructor.
static_constructor<&Test::StaticTest>::c;
forces initialization ofc
during global static initialization.不需要
init()
函数,可以从范围创建std::vector
:但是请注意,类类型的静态会在库中引起麻烦,因此它们应该避免那里。
C++11 更新
从 C++11 开始,您可以这样做:
它在语义上等同于原始答案中的 C++98 解决方案,但您不能在右侧,所以它并不完全优越。 但是,如果您有除
char
、wchar_t
、char16_t
或char32_t
以外的任何其他类型的向量(数组可以写成字符串文字),与 C++98 版本相比,C++11 版本将严格删除样板代码,而不引入其他语法。No need for an
init()
function,std::vector
can be created from a range:Note, however, that statics of class type cause trouble in libraries, so they should be avoided there.
C++11 Update
As of C++11, you can do this instead:
It's semantically equivalent to the C++98 solution in the original answer, but you can't use a string literal on the right-hand-side, so it's not completely superior. However, if you have a vector of any other type than
char
,wchar_t
,char16_t
orchar32_t
(arrays of which can be written as string literals), the C++11 version will strictly remove boilerplate code without introducing other syntax, compared to the C++98 version.在 .h 文件中:
在 .cpp 文件中:
In the .h file:
In the .cpp file:
这是与 Daniel Earwicker 类似的另一种方法,也使用 Konrad Rudolph 的朋友类建议。 这里我们使用内部私有友元实用程序类来初始化主类的静态成员。 例如:
头文件:
实现文件:
这种方法的优点是对外界完全隐藏Initializer 类,保持类中包含的所有内容都被初始化。
Here is another approach similar to Daniel Earwicker's, also using Konrad Rudolph's friend class suggestion. Here we use an inner private friend utility class to initialize the static members of your main class. For example:
Header file:
Implementation file:
This approach has the advantage of completely hiding the Initializer class from the outside world, keeping everything contained within the class to be initialized.
好吧,你可以
不要忘记(在 .cpp 中):
程序仍然会在没有第二行的情况下链接,但初始化程序不会被执行。
Well you can have
Don't forget (in the .cpp) this:
The program will still link without the second line, but the initializer will not be executed.
C++17
从 C++17 开始,您可以使用 lambda 初始化复杂的静态数据成员表达式并声明它们内联:
C++11
自C ++11,您可以使用 lambda 表达式 初始化复杂的静态数据成员:
头文件:
源文件:
C++17
Since C++17, you can initialize complex static data members by using lambda expressions and declaring them inline:
C++11
Since C++11, you can initialize complex static data members by using lambda expressions:
Header file:
Source file:
要获得静态构造函数的等效项,您需要编写一个单独的普通类来保存静态数据,然后创建该普通类的静态实例。
To get the equivalent of a static constructor, you need to write a separate ordinary class to hold the static data and then make a static instance of that ordinary class.