ein*_*ica 28 c++ java initialization static-block equivalent
我有一个带有一些静态成员的类,我想运行一些代码来初始化它们(假设这段代码不能转换成简单的表达式).在Java中,我会这样做
class MyClass {
static int myDatum;
static {
/* do some computation which sets myDatum */
}
}
Run Code Online (Sandbox Code Playgroud)
除非我弄错了,C++不允许这样的静态代码块,对吧?我应该做什么呢?
我想要解决以下两个选项:
对于第二种选择,我在考虑:
class StaticInitialized {
static bool staticsInitialized = false;
virtual void initializeStatics();
StaticInitialized() {
if (!staticsInitialized) {
initializeStatics();
staticsInitialized = true;
}
}
};
class MyClass : private StaticInitialized {
static int myDatum;
void initializeStatics() {
/* computation which sets myDatum */
}
};
Run Code Online (Sandbox Code Playgroud)
但这是不可能的,因为C++(目前?)不允许初始化非const静态成员.但是,至少可以通过表达式将静态块的问题减少到静态初始化的问题......
ein*_*ica 12
事实证明,我们可以实现一个Java风格的静态块,虽然在类之外而不是在它内部,即在翻译单元范围.实施起来有点丑陋,但使用时它非常优雅!
如果你写:
static_block {
std::cout << "Hello static block world!\n";
}
Run Code Online (Sandbox Code Playgroud)
此代码将在您的代码之前运行static_block.hpp.您可以初始化静态变量或执行您喜欢的任何其他操作.因此,您可以在类的main()实现文件中放置这样的块.
笔记:
静态块实现涉及使用函数静态初始化的虚拟变量.您的静态块实际上是该函数的主体.为了确保我们不与其他虚拟变量(例如来自另一个静态块 - 或其他任何地方)发生冲突,我们需要一些宏机制.
#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)
#ifdef __COUNTER__
#define UNIQUE_IDENTIFIER(prefix) EXPAND_THEN_CONCATENATE(prefix, __COUNTER__)
#else
#define UNIQUE_IDENTIFIER(prefix) EXPAND_THEN_CONCATENATE(prefix, __LINE__)
#endif // __COUNTER__
Run Code Online (Sandbox Code Playgroud)
这是将事物放在一起的宏观工作:
#define static_block STATIC_BLOCK_IMPL1(UNIQUE_IDENTIFIER(_static_block_))
#define STATIC_BLOCK_IMPL1(prefix) \
STATIC_BLOCK_IMPL2(CONCATENATE(prefix,_fn),CONCATENATE(prefix,_var))
#define STATIC_BLOCK_IMPL2(function_name,var_name) \
static void function_name(); \
static int var_name __attribute((unused)) = (function_name(), 0) ; \
static void function_name()
Run Code Online (Sandbox Code Playgroud)
笔记:
.cpp- 它不是C++标准的一部分; 在这些情况下,上面的代码使用__COUNTER__,也有效.GCC和Clang都支持__LINE__.__COUNTER__可被丢弃,或更换__attribute ((unused)),如果你有一个C++编译器11不喜欢GCC风格的未使用的扩展.[[unused]],但是无法确保何时相对于其他静态初始化发生这种情况.Rei*_*ica 10
对于#1,如果你真的需要初始化进程启动/库加载时,你将不得不使用特定于平台的东西(例如Windows上的DllMain).
但是,如果它足以让您在执行静态的同一.cpp文件中的任何代码之前运行初始化,则以下内容应该有效:
// Header:
class MyClass
{
static int myDatum;
static int initDatum();
};
Run Code Online (Sandbox Code Playgroud)
// .cpp file:
int MyClass::myDatum = MyClass::initDatum();
Run Code Online (Sandbox Code Playgroud)
这样,initDatum()保证在.cpp执行该文件的任何代码之前调用.
如果您不想污染类定义,还可以使用Lambda(C++ 11):
// Header:
class MyClass
{
static int myDatum;
};
Run Code Online (Sandbox Code Playgroud)
// .cpp file:
int MyClass::myDatum = []() -> int { /*any code here*/ return /*something*/; }();
Run Code Online (Sandbox Code Playgroud)
不要忘记最后一对括号 - 实际上是调用lambda.
至于#2,有一个问题:你不能在构造函数中调用虚函数.你最好在课堂上手工完成,而不是使用它的基类:
class MyClass
{
static int myDatum;
MyClass() {
static bool onlyOnce = []() -> bool {
MyClass::myDatum = /*whatever*/;
return true;
}
}
};
Run Code Online (Sandbox Code Playgroud)
假设该类只有一个构造函数,那就可以正常工作; 它是线程安全的,因为C++ 11保证了初始化静态局部变量的安全性.
您可以在C++中初始化静态数据成员:
#include "Bar.h"
Bar make_a_bar();
struct Foo
{
static Bar bar;
};
Bar Foo::bar = make_a_bar();
Run Code Online (Sandbox Code Playgroud)
您可能不得不考虑跨翻译单元依赖关系,但这是一般方法.
| 归档时间: |
|
| 查看次数: |
12053 次 |
| 最近记录: |