代码之家  ›  专栏  ›  技术社区  ›  Jon Purdy

用于更改静态成员初始化行为的干净语法

  •  1
  • Jon Purdy  · 技术社区  · 14 年前

    class C {
    
        public C() { /* Instance Construction */ }
        static { /* Static Initialisation */ }
        { /* Instance Initialisation */ }
    
    }
    

    static 阻止。它让我想到了影响许多C++新手的静态初始化顺序问题,以及它的典型解决方案,例如将一个静态成员打包成一个自由函数,或者使用GNU。 __attribute__((init_priority(n))) 分机。

    我正在寻找一些方法来编写一个将被调用的方法 自动

    到目前为止,这是我想到的最好的:

    class C {
    private:
    
        class Static {
        public:
    
            Static();
    
            int i;
            Foo foo;
    
        };
    
    public:
    
        static Static statics;
    
        // ...
    
    };
    
    C::Static::Static() : i(42), foo("bar") {}
    

    也就是说,将所有静态成员包装在一个类中,并创建该类的静态实例,其构造函数充当静态初始化函数。很容易修改它来实例化 statics 成员只在创建实例时使用,甚至确保正确销毁静态成员。

    当然,问题是 C::foo 变成 C::statics.foo ,这很不自然。有没有一种方法可以绕过笨拙的语法,或者有更好的解决方案?

    2 回复  |  直到 14 年前
        1
  •  3
  •   John Dibling    14 年前

    对我来说,这样的努力在很大程度上是不值得的。你真的没有提高可读性,不寻常的构造会让未来维护你的代码的程序员感到困惑,而新类的加入增加了复杂性,从而使你暴露在更多错误的可能性中。

    我可以看到,您很少需要或想要控制静态初始化的顺序,或者出于其他原因,打包所有静态。但从可读性的角度来看,我更喜欢久经考验的:

    class C
    {
    private:
      static int i;
      static Foo foo;
    };
    
    int C::i = 42;
    Foo C::foo("bar");
    

    如果你有很多这样的静态数据,为什么不把它们全部发送到自己的CPP文件中呢?但话说回来,如果你真的有那么多的静力学,我想知道是否有一些错误的整体设计在第一位。。。

        2
  •  0
  •   Matthieu M.    14 年前

    要明确的是,全局变量通常有三个问题(不仅仅是静态变量):

    • 初始化命令失败
    • 破坏令惨败
    • 多线程多初始化

    当然,大多数时候破坏并不是什么问题,但它仍然存在。

    新的C++ 0x是线程感知的,因此存在一些增益(特别是对于多线程问题的本地静态变量)。

    随着C++ 0x的出现,下面的代码可能只会遭受“销毁命令FASASCO”的影响…当然,除非你在初始化时有一个循环引用。

    // foo.h
    class Foo
    {
    public:
      static Foo& Instance() { static Foo M; return M; }
    
    private:
      Foo();
    };
    
    // bar.h
    // idem for Bar
    
    // foo.cpp
    Foo::Foo() { Bar& bar = Bar::Instance(); .... }
    

    这很好:因为实例是按需创建的,所以当您需要时,它保证它在那里,并且即使在多个线程同时尝试访问函数时,C++也保证了行为。

    现在,“破坏令惨败”怎么办?

    好吧,对象的解构顺序与它们的构造顺序相反,因此如果你需要在解构器中访问一个对象,那么在构造函数中访问它只是为了确保它在你之前被构建,并且一切正常。

    当然,没有C++ 0x的东西有点令人耳目一新。为了避免多线程问题,唯一的建议是在应用程序仍然是单线程(main)时首先访问所有变量。这样所有实例都被创建,不再存在任何并发问题。