有些時候,我們會希望在某個全域物件的建構式當中使用另一個全域物件,但這樣做在C++當中是很危險的。

例如我們希望有個log物件,這個物件在整個專案是獨一無二的,而且保證在第一次使用之前就已經建構好、在最後一次使用之後才釋放。假如有個全域物件建構到一半需要將遭遇的狀況記錄下來,可以直接使用此log物件,而不必擔心log尚未建構;同樣的,我們也可能希望在全域物件解構的時候紀錄一些事情。

困難之處在於,C++對於全域物件的建構順序並沒有一定的規律。C++只保證同一個編譯單元的順序是由上而下,而解構一定遵守和建構相反的順序,但是跨檔案的建構順序完全無法掌握。

這個問題在很久以前就已經有解決之道,並且運用到標準程式庫的cin、cout、cerr、clog等。

header file

class Log {
friend class Initializer;
public:
void Print(const char* msg) {
std::fputs(msg, onlyFile_);
}

private:
static std::FILE* onlyFile_;
};

class Initializer {
public:
Initializer();
~Initializer();
private:
static int ref_;
};

extern Log log;
static Initializer init;

implementation

std::FILE* Log::onlyFile_ = 0;
int Initializer::ref_ = 0;

Initializer::Initializer() {
if (0 == ref_++) {
Log::onlyFile_ = fopen("somefile.txt", "w");
}
}

Initializer::~Initializer() {
if (0 == --ref_) {
fclose(Log::onlyFile_);
}
}

使用


#include "log.hpp"
.....
log.Print("something happend.\n");

如此就可以在其他地方使用獨一無二、保證早到晚退的log物件了。這個手法在類別結構和static物件所有權方面有多種變化,不過原理都是利用#include的同時植入一個小小的標記。

此法通常叫做Schwarz Counter或Nifty Counter,是Jerry Schwarz為了解決iostream問題而發明的。

arrow
arrow
    全站熱搜

    novus 發表在 痞客邦 留言(1) 人氣()