一尘不染

与Java静态块等效的C ++习惯用法是什么?

java

我有一个带有一些静态成员的类,并且我想运行一些代码来初始化它们(假设此代码无法转换成简单的表达式)。在Java中,我只会

class MyClass {
    static int myDatum;

    static {
        /* do some computation which sets myDatum */
    }
}

除非我弄错了,否则C ++不允许使用此类静态代码块,对吗?我应该怎么做呢?

我想要以下两个选项的解决方案:

  1. 初始化在进程加载时发生(或在加载带有此类的DLL时)。
  2. 初始化发生在第一次实例化该类时。

对于第二种选择,我在想:

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 */
    }
};

但这是不可能的,因为C ++(目前?)不允许初始化非常量静态成员。但是,至少这可以通过表达式将静态块的问题减少到静态初始化的问题…


阅读 241

收藏
2020-09-08

共1个答案

一尘不染

您也可以在C ++中具有静态块-外部类。

事实证明,我们可以实现Java样式的静态块,尽管它是在类外部而不是在类内部,即在翻译单元范围内。内幕的实现有点丑陋,但是使用起来非常优雅!

可下载的版本

该解决方案现在有一个GitHub存储库,其中包含一个头文件:static_block.hpp

用法

如果您写:

static_block {
    std::cout << "Hello static block world!\n";
}

此代码将在您的之前运行main()。您可以初始化静态变量,也可以执行其他任何操作。因此,您可以在类的.cpp实现文件中放置这样的块。

笔记:

  • 必须 用花括号将静态块代码括起来。
  • 在C ++中不能保证执行静态代码的相对顺序。

实作

静态块实现涉及使用函数静态初始化的虚拟变量。您的静态块实际上是该函数的主体。为了确保我们不会与其他虚拟变量发生冲突(例如,来自另一个静态块或其他任何地方),我们需要一些宏机制。

#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__

这是将内容组合在一起的宏工作:

#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()

笔记:

  • 一些编译器不支持__COUNTER__-它不是C 标准的一部分;它不支持C 。在这种情况下,以上代码__LINE__也会使用。GCC和Clang支持__COUNTER__
  • 这是C 98;您不需要任何C 11/14/17构造。但是,即使不使用任何类或方法,它 也不是 有效的C语言。
  • __attribute ((unused))可被丢弃,或更换[[unused]],如果你有一个C ++编译器11,其不喜欢的GCC风格的未使用的扩展。
  • 这不会避免或不利于静态初始化顺序fiasco,因为虽然您知道静态块将在之前执行main(),但不能保证相对于其他静态初始化确切的执行时间。

Live Demo

2020-09-08