概要
C++において、main関数開始後に呼び出される関数内のローカルな静的オブジェクトのデストラクタからローカルでない静的オブジェクトを利用するコードを書いても大丈夫かが分からなかったので調べていました。
イメージとしては下記のようなログ管理クラスをローカルでない静的オブジェクトとしているソースコードが大丈夫かどうかです。
#include <iostream>
using namespace std;
void LogPrint(const char* str);
class A
{
public:
A()
{
LogPrint("A::A()");
}
~A()
{
LogPrint("A::~A()");
}
void Print()
{
cout << "A::Print()"<< endl;
}
};
A& GetInstanceA()
{
// ローカルな静的オブジェクト
static A a;
return a;
}
int main()
{
GetInstanceA().Print();
return 0;
}
#include <iostream>
using namespace std;
// ログ管理用クラス
class Log
{
public:
Log()
{
cout << "Log::Log()" << endl;
}
~Log()
{
cout << "Log::~Log()" << endl;
}
void Print(const char* str)
{
cout << str << endl;
}
};
// ローカルでない静的オブジェクト
static Log s_log;
void LogPrint(const char* str)
{
s_log.Print(str);
}
下記の通りビルドして実行してみると、A::~A()
→Log::~Log
の順序となっており、この場合だと大丈夫した。しかし、もし不定であり、Log::~Log
→A::~A()
となってしまうとまずい事態になりそうですが、大丈夫なのでしょうか。
$ clang++ main.cpp Log.cpp
$ ./a.out
Log::Log()
A::A()
A::Print()
A::~A()
Log::~Log()
結論としては、静的オブジェクトの破棄順序は初期化順序の逆と決まっているため、大丈夫でした。
ローカルでない静的オブジェクトはmain関数が呼ばれる前に初期化され、main関数開始後に呼び出される関数内のローカルな静的オブジェクトは関数呼び出しのタイミングで初期化されます。ローカルな静的オブジェクトのデストラクタからローカルでない静的オブジェクトを用いても、ローカルでない静的オブジェクトが破棄されていないため、問題ありませんでした。
下記に検証用のソースコードと実行結果を載せます。
検証
以下の3つのcppを用います。
#include <iostream>
using namespace std;
// 外部の翻訳単位で定義されている関数
void XFunc();
void XFunc2();
void YFunc();
void YFunc2();
int main()
{
XFunc();
YFunc2();
YFunc();
XFunc2();
cout << "-------------" << endl;
cout << "main()関数終了" << endl;
cout << "-------------" << endl;
return 0;
}
#include <iostream>
using namespace std;
class X
{
public:
X(const char* str)
: m_str(str)
{
cout << "X:X()" << ":" << m_str << endl;
}
~X()
{
cout << "X::~X()" << ":" << m_str << endl;
}
private:
const char* m_str;
};
void XFunc()
{
cout << "XFunc()" << endl;
static X localX("ローカルな静的オブジェクト");
}
void XFunc2()
{
cout << "XFunc2()" << endl;
static X localX2("ローカルな静的オブジェクト2");
}
static X exX("ローカルでない静的オブジェクト");
#include <iostream>
using namespace std;
class Y
{
public:
Y(const char* str)
: m_str(str)
{
cout << "Y:Y()" << ":" << m_str << endl;
}
~Y()
{
cout << "Y::~Y()" << ":" << m_str << endl;
}
private:
const char* m_str;
};
void YFunc()
{
cout << "YFunc()" << endl;
static Y localY("ローカルな静的オブジェクト");
}
void YFunc2()
{
cout << "YFunc2()" << endl;
static Y localY2("ローカルな静的オブジェクト2");
}
static Y exY("ローカルでない静的オブジェクト");
下記の通り、ビルドして実行してみると、静的オブジェクトの破棄順序は初期化順序の逆となっていることが確認できました。
$ clang++ main.cpp X.cpp Y.cpp
$ ./a.out
X:X():ローカルでない静的オブジェクト
Y:Y():ローカルでない静的オブジェクト
XFunc()
X:X():ローカルな静的オブジェクト
YFunc2()
Y:Y():ローカルな静的オブジェクト2
YFunc()
Y:Y():ローカルな静的オブジェクト
XFunc2()
X:X():ローカルな静的オブジェクト2
-------------
main()関数終了
-------------
X::~X():ローカルな静的オブジェクト2
Y::~Y():ローカルな静的オブジェクト
Y::~Y():ローカルな静的オブジェクト2
X::~X():ローカルな静的オブジェクト
Y::~Y():ローカルでない静的オブジェクト
X::~X():ローカルでない静的オブジェクト