Skip to content

Construct On First Use

More C++ Idioms/Construct On First Use

Motivation

Static objects that have non-trivial constructors must be initialized before they are used. It is possible to access an uninitialized non-local static object before its initialization if proper care is not exercised.

NOTE: non-trivial constructors

#include <iostream>
using namespace std;
struct Bar
{
    Bar()
    {
        cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "Bar::Bar()\n";
    }
    void f()
    {
        cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "Bar::f()\n";
    }
};
struct Foo
{
    Foo()
    {
        bar_.f();
    }
    static Bar bar_;
};

Foo f; // 会调用`Foo()`,进而调用 `f()`

Bar Foo::bar_; // definition of static member bar_,此时会调用`Bar()`

int main()
{
    cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "main\n";
}
// g++ -Wall -pedantic test.cpp

NOTE:

1、输出如下:

[test.cpp][11]Bar::f()
[test.cpp][7]Bar::Bar()
[test.cpp][27]main

2、这是典型的static initialization order ‘fiasco’ (problem)

显然,在构造函数执行之前,就已经执行了函数f(),显然这是错误的。

静态成员,内存应该已经分配了,直到运行的时候,才调用构造函数对它进行初始化。

Solution and Sample Code

NOTE:

1、下面的solution,说白了其实就是使用singleton来替代static global

Construct on first use using dynamic allocation

#include <iostream>
using namespace std;
struct Bar
{
    Bar()
    {
        cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "Bar::Bar()\n";
    }
    void f()
    {
        cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "Bar::f()\n";
    }
};
struct Foo
{
    Foo()
    {
        bar().f();
    }
    Bar& bar()
    {
        static Bar *b = new Bar();
        return *b;
    }
};

Foo f; // 会调用`Foo()`,进而调用 `Bar()`

int main()
{
    cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "main\n";
}
// g++ -Wall -pedantic test.cpp

NOTE: 输出如下:

[test.cpp][7]Bar::Bar()
[test.cpp][11]Bar::f()
[test.cpp][31]main

Construct on first use using local static

#include <iostream>
using namespace std;
struct Bar
{
    Bar()
    {
        cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "Bar::Bar()\n";
    }
    void f()
    {
        cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "Bar::f()\n";
    }
};
struct Foo
{
    Foo()
    {
        bar().f();
    }
    Bar& bar()
    {
        static Bar b;
        return b;
    }
};

Foo f; // 会调用`Foo()`,进而调用 `Bar()`

int main()
{
    cout << "[" << __FILE__ << "]" << "[" << __LINE__ << "]" << "main\n";
}
// g++ -Wall -pedantic test.cpp

NOTE: 输出如下:

[test.cpp][7]Bar::Bar()
[test.cpp][11]Bar::f()
[test.cpp][31]main