Singletonを実現するgetInstanceが推奨できない理由

id:nicht-seinさんからコメントがあったので書いてみます。
まず、コメントにも書いてくださったようによく見かけるこれ
(僅かに手を加えました)

class CHoge {
private:
    CHoge() : value_(100) {};
    ~CHoge(){};
    int value_;
public:
    void setHoge(int value) { value_ = value; }
    int getHoge() const { return value_; }
    static CHoge& getInstance() {
        static CHoge instance_;
        return instance_;
    }
};

他からnewされないようにコンストラクタがprivateになってます。
勿論、スタックに置くためにCHoge hoge;もできません。
でも、実はこれはインスタンス化できます。

    printf("%d\n", CHoge::getInstance().getHoge() );
    CHoge* hoge = new CHoge(CHoge::getInstance());
    hoge->setHoge(999);
    printf("%d\n", hoge->getHoge() );
    printf("%d\n", CHoge::getInstance().getHoge() );

こんな感じ。
実行すると、インスタンスが二つあることがわかる。
なぜnewできるのかというと、暗黙のコピーコンストラクタがあるから。
(nicht-seinさんが挙げた例は単に省略していた可能性があります)
結局、これらを完全に防御する手段をするのは割と面倒なことです。
また、こうしたクラスを作っても、
似たような機能を持つSingletonでないクラスをつくられたら終わりです。

で、結論はそういうことではなくて、

結局のところ、
安易にgetInstanceのクラスを増やすと
グローバル変数」を増やしているのと変わらないということ
それは要するに「密結合」であり、
「依存度の高さを不用意に高めている」ことに他ならない。

だいたい、
「本当にインスタンスが一つであることを完全に保証しなければならない」
ことってそもそもそんなに多発するケースなんだろうか?
そして、そのためにgetInstanceが溢れる世の中になっていいの?

if (Hoge::getInstance().getHoge() > 0) {
    if (Foo::getInstance().isFoo() && Bar::getInstance().isBar()) {
        ...
    }
}

極端な例だけれども、
こんなプログラムは見たくない。

if (Hoge().getHoge() > 0) {
    if (Foo().isFoo() && Bar().isBar()) {
        ...
    }
}

でも本質は一緒だ。
元々、Singletonは
「そのクラス自身を見たとき」
「そのインスタンスが一つしかない事を『表現』する」
ためにあるのであって、

他のクラスから見たとき、それが一つであるかどうかはどうでもいいし、
他のクラスからお手軽に(グローバル的に)アクセスすることを赦すために存在するわけじゃない。
そのインスタンスが単一のものであるかどうかなんてことは、
「そのクラス内で閉じているべき」なのだ。

ちょっと趣旨は違うけど

Singletonは、所詮、羊の皮を被ったグローバル変数であり、
相互依存や循環依存によって壊されることに注意しよう。

(初期化の依存関係について、C++ Coding Standardsより抜粋)

実例に沿ってSingletonとんとん

例えば、
ゲーム的に表現してしまうんですが、
ScriptEngine
というクラスがあるとして、
スクリプトをロードして実行する機能を持つとする。

一つのスクリプトデータに関して一つのインスタンスを持つ訳ではなく、
ロードしたデータに関連したハンドル、
ないしは戻り値として関連づけられたインスタンスを返し、
それらが各スクリプト実行の責務を負うとする。

こういうとき、
ScriptEngineというクラスは世界に一つだろうが、
何もSingletonにして全世界に公開する必要はない。

もっと言えば、
EnemyFactoryなんちゃら
みたいなものがあったとして、
これも世界に一つだろうが、
Singletonである必要はない。

クラスはクラスの必要最低限の箇所で稼働すべきで、
安直にグローバルになるべきではない。

GameParameter
とかもそうかも。

グローバル変数にしたいからSingletonにしてないか?」
をまず考えるべきだと思う。

とか、そういう益体のない話なのでした。
まる。

今日、
若い子に「別にグローバル変数でも良いんじゃないですか」とか言われたけどね ^_^;