[c++]名前空間を使ったモノステートパターンについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
K_Tarou
記事: 22
登録日時: 11年前

[c++]名前空間を使ったモノステートパターンについて

#1

投稿記事 by K_Tarou » 10年前

お世話になっております。
こちらのサイト ( http://www.geocities.co.jp/bleis_tift/c ... leton.html ) の記事を参考に勉強しようとしていましたところ、
モノステートパターンはクラス以外にも、名前空間と無名名前空間を使って実装できるということが書かれていました。

こちらの過去のトピック ( http://dixq.net/forum/viewtopic.php?t=8433&p=69002 ) で
h2so5 さんが提示されていたコード(下記コード)が正に、その名前空間を使ったモノステートパターンではないかと思ったのですが、
名前空間の中で無名名前空間を使用する意味 ・ 利点がよく分かりません。
ヘッダファイルに記述して、複数箇所でインクルードした際に変数の実体定義によるリンクエラーを防止できることは確認しましたが、そういう理由ではなさそうですし。


オブジェクト指向に関する知識等が根本的に不足しているせいかもしれませんが、自分でコードを弄って考えても、この無名名前空間の役割が思い浮かびませんでした。
すみませんが、教えて頂けませんでしょうか? よろしくお願いします。

コード:


/* 上記トピックで、h2so5 さんが提示されたコードです */

//キーボードクラス
namespace Keyboard{
    namespace{
        int key[256];
    }
    void Key_Update();
    int Key_Get(int KeyCode);
}

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: [c++]名前空間を使ったモノステートパターンについて

#2

投稿記事 by tk-xleader » 10年前

 もしモノステートパターンをクラスで実装するとすれば、こういうコードになりますね。

コード:

class Keyboard{
private: //※ここ
	static int key[256];
public:
	void keyUpdate(){/*.....*/}
	int getKeyState(){/*....*/}
private: //以下、インスタンスの生成を防止する。
	Keyboard();
	Keyboard(const Keyboard&);
	void operator=(const Keyboard&);
};
 ここで、名前空間での実装においては、名前空間Keyboardがclass名に対応していることになりますね。では、Keyboard内の無名名前空間は何に対応しているのかというと、「//※ここ」で示したprivateです。
 無名名前空間は、宣言をファイルスコープ(正確には、一翻訳単位内スコープですが…)とするという意味があります。そうすれば、keyに対して別のC++ファイルからアクセスされることが無くなります。
 そこで、ヘッダは関数宣言だけ書いておいて、Cppファイルに実装と変数定義を書いておくというファイル構成をとることになります。

Keyboard.h

コード:

namespace Keyboard{
	void keyUpdate();
	int getKeyState();
};
Keyboard.cpp

コード:

namespace Keyboard{
	namespace{
		static int key[256];
	}
	void keyUpdate(){/*.....*/}
	int getKeyState(){/*....*/}
};

K_Tarou
記事: 22
登録日時: 11年前

Re: [c++]名前空間を使ったモノステートパターンについて

#3

投稿記事 by K_Tarou » 10年前

tk-xleader さん
ご回答ありがとうございます。返答が遅くなり、申し訳ありません。
無名名前空間がクラスにおける private指定の役割を果たすというご指摘は理屈として、とても納得できました。

できたのですが、ご提示していただいたコードについて、以下のように無名名前空間の部分をコメントアウトしても
実装ファイル以外の外部ファイル( 下記の Keyboard_Sub.cpp )からアクセスができない状態となりました。

Keyboard.h

コード:

namespace Keyboard{
	void keyUpdate();
    const int* getKeyState();
}
Keyboard.cpp (実装ファイル)

コード:

#include "Keyboard.h"

namespace Keyboard{
	//namespace{           <--- 無効化
		int key[256];
	//}
        void keyUpdate(){/*.....*/}
        const int* getKeyState(){ return key; }
}
Keyboard_Sub.cpp

コード:

/* 無名名前空間内にアクセスできるか確認するだけのファイル */
#include "Keyboard.h"

void set(int num){
	Keyboard::key[0] = num;
}
無名名前空間の有無に関わらず、アクセス不可の状態ということになり、無名名前空間が無くても問題がないかのように感じてしまいました。
そこで試しに2つのコードを書きましたが、以下のような状態になりました。
► スポイラーを表示
( 1 ) のコードでは無名名前空間で囲っていても外部からアクセスができました。
( 2 ) のコードでは無名名前空間の有無によって、アクセスの可否が変わりました。 ( 有り = アクセス不可   無し = アクセス可 )

これらの挙動の違いを考えていたのですが、余計に混乱してしまいました。
ヘッダファイルをインクルードして名前空間を取り込んだ際に、ネストされた無名名前空間の名前を指定出来ない為に
アクセスを封じることができるといった理屈は何となく理解できるのですが、イマイチすっきり納得できません。


オブジェクト指向以前に名前空間についての質問になってしまいますが、どうか教えていただけませんか? よろしくお願いします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: [c++]名前空間を使ったモノステートパターンについて

#4

投稿記事 by h2so5 » 10年前

Keyboard.h を以下のようにしてみてください。これで無名名前空間の有り・無しの違いが分かります。

コード:

namespace Keyboard {
    extern int key[256];
    void keyUpdate();
    const int* getKeyState();
}
K_Tarou さんが書きました: ( 1 ) のコードでは無名名前空間で囲っていても外部からアクセスができました。
( 2 ) のコードでは無名名前空間の有無によって、アクセスの可否が変わりました。 ( 有り = アクセス不可   無し = アクセス可 )
( 1 ) ヘッダファイルに無名名前空間を書いても、それをインクルードしたソースは同じ翻訳単位ですから「外部」ではありません。
( 2 ) については状況がよくわかりません。ヘッダをインクルードしているソースではどのように定義されているのでしょうか。

K_Tarou
記事: 22
登録日時: 11年前

Re: [c++]名前空間を使ったモノステートパターンについて

#5

投稿記事 by K_Tarou » 10年前

h2so5 さん
ご回答ありがとうございます。
h2so5 さんが書きました: ( 1 ) ヘッダファイルに無名名前空間を書いても、それをインクルードしたソースは同じ翻訳単位ですから「外部」ではありません。
( 2 ) については状況がよくわかりません。ヘッダをインクルードしているソースではどのように定義されているのでしょうか。
説明が下手ですみません、( 2 )の状況は以下のようになっております。

コード:

/***   名前空間 "Keyboard" にアクセスするファイル ( Keyboard_Sub.cpp )   ***/
#include "Keyboard.h"

void set(int num){
	Keyboard::key[0] = num;      /***   ヘッダファイルで無名名前空間が存在するとエラーに、存在しなければアクセスできる   ***/
}


/***   extern 宣言した変数を定義するファイル ( Keyboard.cpp )   ***/
#include "Keyboard.h"

namespace Keyboard{
	namespace{
		int key[256];      /*** 定義 ***/
	}

	void keyUpdate(){/*.....*/}
    const int* getKeyState(){ return key; }
}
上記のコードでは、 ヘッダファイルで extern指示子を付与して宣言を行っている状態で、
この状態では、ご指摘していただいた通り、ヘッダファイルに記述した、無名名前空間の有無による違いが明確に表れました。
これについては単純に、通常の名前空間のネストとは違って無名名前空間では名前を指定できないために、アクセスができないという理屈なのだろうと考えました。


しかし、( 1 ) ( 2 ) 共に同じようにインクルードしていて、同一の翻訳単位になっている (?) 筈なのに
何故、アクセスの可否に違いが生じるのかが分からないという状態でした。

( 2 ) のように extern宣言を行い、外部( 上記のKeyboard.cpp )で定義を行っている場合は、Keyboard_Sub.cpp と同一の翻訳単位とは見なされないのでしょうか?
理解力が乏しくてすみません。

アバター
tk-xleader
記事: 158
登録日時: 13年前
連絡を取る:

Re: [c++]名前空間を使ったモノステートパターンについて

#6

投稿記事 by tk-xleader » 10年前

 まず、include指令は単にそのファイルの内容をテキスト置換するだけです。翻訳単位はcppファイル毎に生成されますから、同じヘッダファイルをincludeしていたとしても、別々の翻訳単位にそれぞれ同じ「文字列」を挿入しているに過ぎません。

 そのうえで、無名名前空間の内部で外部結合を指定するexternをつけて変数を宣言するとどうなるかといいますと、無名名前空間によってその変数名は翻訳単位ごとに一意の識別子としての扱いになってしまいます(そのおかげでファイルスコープとして扱える)ので、結局、同一ファイル・同一の名前空間に属する無名名前空間にある同じ名前の変数によってでしかその実体の解決は出来ないことになります。
 というわけで、(2)の場合、無名名前空間を外した場合は、Keyboard_sub.cppの翻訳単位の

extern int key[256];

という宣言は、別の翻訳単位であるKeyboard.cppにあるkeyを参照することが出来ますが、無名名前空間の中に入れた場合は、同一ヘッダファイルにある宣言であっても、翻訳単位ごとに独立した識別子として扱われるため、Keyboard.cppにあるkeyとは別物となってしまい、これを参照することはできないのです。

K_Tarou
記事: 22
登録日時: 11年前

Re: [c++]名前空間を使ったモノステートパターンについて

#7

投稿記事 by K_Tarou » 10年前

tk-xleader さん
ご回答ありがとうございます。

無名名前空間の内部リンケージ化によって、extern宣言した変数の参照が出来なくなっていた、ということなのですね。
外部リンケージにする extern を付けている状態と、そのまま実体を定義している状態での、無名名前空間の有無による違いが疑問だったのですが、
とてもすっきり理解し、納得できました。本当にありがとうございました!

h2so5 さんも態々、丁寧に回答してくださってありがとうございました!
そして、分かり難い説明になってしまい、すみませんでした。

お二方共、本当に助かりました。
プログラムについての勉強だけでなく、人に簡潔に意図を伝える努力もしていきたいと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る