Cpp Static Initialization Order Fiasco

Static initialization order fiasco

  • Construct On First Use Idiom

    • If the object does not need to be destructed, uses a static pointer.

      1
      2
      3
      4
      5
      6
      7
      // File x.cpp
      #include "Fred.h"
      Fred& x()
      {
      static Fred* ans = new Fred();
      return *ans;
      }
      1
      2
      3
      4
      5
      6
      7
      8
      // File Barney.cpp
      #include "Barney.h"
      Barney::Barney()
      {
      // ...
      x().goBowling();
      // ...
      }

      This approach is that the Fred object is never destructed.
      The Construct On First Use Idiom uses a pointer and intentionally leaks the object.

    • If you need to destruct the object and do not care the order of static objects deinitialization, uses a static object.

      1
      2
      3
      4
      5
      6
      7
      // File x.cpp
      #include "Fred.h"
      Fred& x()
      {
      static Fred ans; // was static Fred* ans = new Fred();
      return ans; // was return *ans;
      }

      It does not leak the object, but it does not control the order of static deinitialization, so it is (very!) unsafe to use the object during static deinitialization, that is, from a destructor of another statically declared object.

  • Nifty Counter

    It is a more complicated solution that solves problems mentioned above.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Stream.h
    #ifndef STREAM_H
    #define STREAM_H

    struct Stream {
    Stream ();
    ~Stream ();
    };
    extern Stream& stream; // global stream object

    static struct StreamInitializer {
    StreamInitializer ();
    ~StreamInitializer ();
    } streamInitializer; // static initializer for every translation unit

    #endif // STREAM_H
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    // Stream.cpp
    #include "Stream.h"

    #include <new> // placement new
    #include <type_traits> // aligned_storage

    static int nifty_counter; // zero initialized at load time
    static typename std::aligned_storage<sizeof (Stream), alignof (Stream)>::type
    stream_buf; // memory for the stream object
    Stream& stream = reinterpret_cast<Stream&> (stream_buf);

    Stream::Stream ()
    {
    // initialize things
    }
    Stream::~Stream ()
    {
    // clean-up
    }

    StreamInitializer::StreamInitializer ()
    {
    if (nifty_counter++ == 0) new (&stream) Stream (); // placement new
    }
    StreamInitializer::~StreamInitializer ()
    {
    if (--nifty_counter == 0) (&stream)->~Stream ();
    }

    The header file of the Stream class must be included before any member function can be called on the Stream object.
    An instance of the StreamInitializer class is included in each compilation unit. Any use of the Stream object follows the inclusion of the header, which ensures that the constructor of the initializer object is called before the Stream object is used.

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2015-2024 RivenZoo
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信