ぼく用あれこれまとめ

SAFE_DELETE()系のdefineマクロ廃止

最終更新:

bokuyo

- view
管理者のみ編集可

SAFE_DELETE()系の#defineマクロ廃止


なぜ#defineマクロを廃止するの?

  • const定数を使う理由と同じで、型が不鮮明であるため、なるべくC++では使わないほうがいいかと。
  • マクロはプリプロセス実行なので型チェックが行われませんし、名前空間も存在しません。とても危険です。

こういうの。

#ifndef SAFE_DELETE
#define SAFE_DELETE(p)       { if(p!=NULL) { delete (p);     (p) = NULL; } }
#endif

#ifndef SAFE_DELETE_ARRAY
#define SAFE_DELETE_ARRAY(p) { if(p!=NULL) { delete[] (p);   (p) = NULL; } }
#endif

#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p)      { if(p!=NULL) { (p)->Release(); (p) = NULL; } }
#endif

じゃあどうするの?

  • templateを使います(キリッ


そもそも、なんでSAFE_DELETE()を使うの?

  • 便利だから。
int main(){

	int* unco = new int(4);			//newで確保しちゃう。
	std::cout << *unco << std::endl;
	delete (unco);				//解放しちゃう。
	unco = NULL;				//危険だからNULLを入れる。

	return 0;
}
  • deleteで解放した後にuncoポインタが確保していた領域をさしてるのは危ないよね、ってことでNULLをいれる。
  • だから、deleteしたあとわざわざNULLいれるの忘れがちだから、いっそのことSAFE_DELETEマクロ作っちゃおうぜ、的な。
  • しかも、SAFE_DELETEなら、どんな型でもいけちゃうぜ!みたいな。だからみんな便利便利~って言って使ってるの。

この場合のint型のSAFE_DELETE関数を実際に作っちゃおう。

void SafeDelete(int*& p)
{
	if(p != NULL){
		delete (p);
		(p) = NULL;
	}
}

int main(){

	int* unco = new int(4);
	std::cout << *unco << std::endl;
	SafeDelete(unco);

	return 0;
}
  • こんな感じかな?もし引数の「int型のポインタの参照」をみて吐き気がしたならきっとぼくと友達になれるかも。
  • ↓これでいいんじゃね?と思ってる人、
void SafeDelete(int* p)			//p = 0x1b。代入!
{
	if(p != NULL){
		delete (p);		//0x1bに確保されたint型のデータ(=156)をdelete
		(p) = NULL;		//0x1bにNULLを入れる。←意味ない。本来なら0x3fにNULLをいれなきゃ。
	}
}

int main(){

	int* unco = new int(156);	//uncoが指すアドレスは0x1b, unco自体のアドレスは0x3f
	SafeDelete(unco);		//SafeDeleteに、「0x1b」を渡した

	return 0;
}
  • これを動かすと、SafeDelete(unco)をしても、uncoにはNULLが入ってません。残念。
  • なので、SafeDelete(int*&)のように、引数は「ポインタの参照」を使うのです。
  • 深く考えずに、こう考えるのが早い?
void SafeDelete(int x)  //これじゃだめ。(int& x)をパラメータにする
{
	x = 0;
}

int main(){

	int Number = 4;
	SafeDelete(Number);

	return 0;
}
  • SafeDeleteを使って、Numberに0を入れたいんだけど、これどう考えても、0入らないよね。
  • だから参照を使ったの。「"int"型」から「"int型のポインタ"型」に変わっただけ。
  • っていうか、どうでもいいことかきまくりだね。

で、これを元にSafeDelete関数をtemplateで書いちゃうだけ。

  • 「int」の部分をどんな型でもいけるようにtemplate使って書き変えちゃうだけ。
template <typename T>
void SafeDelete(T*& p)
{
	if(p != NULL){
		delete (p);
		(p) = NULL;
	}
}
  • ね、簡単でしょ?

そんでもってインライン関数化。

  • #defineマクロと同じようになるべくプリプロセスあたりで展開してコードを置き換えてもらいたい願望。
  • そこでインライン関数を使うことに。
  • インライン関数なら、プリプロセスではないけどコンパイル時に展開してコードに置き換えられるからとても便利。
  • 短いコードじゃないとなかなかインライン化されないけど、これくらいならきっとコンパイラたんがなんとかしてくれるはず。
template <typename T>
inline void SafeDelete(T*& p){
	if(p != NULL) {
		delete (p);
		(p) = NULL;
	}
}
  • これで#defineマクロと同じくらいパワフルな関数ができました。
  • (なんか「パワフルな~」ってgems的な言い方だよね)

たぶん完成。はぴはぴはっぴー。

template <typename T>
inline void SafeDelete(T*& p){
	if(p != NULL) {
		delete (p);
		(p) = NULL;
	}
}

template <typename T>
inline void SafeDeleteArray(T*& p){
	if(p != NULL) {
		delete[] (p);
		(p) = NULL;
	}
}

template <typename T>
inline void SafeRelease(T*& p){
	if(p != NULL) {
		(p)->Release();
		(p) = NULL;
	}
}
  • もうこれを機に#defineマクロを使わないようにしようね。お兄さんとの約束。


もう一度考察してみた

  • SAFE_DELETEマクロでは「if(p){}」とあるように、NULLポインタをdeleteしないようにしてある(のが一般的)。
    • 別にdelete演算子にNULLポインタ渡しちゃっていいんじゃない?無害でしょ?
    • delete演算子にNULLポインタを渡しても何も起きないのがC++の仕様らしいけど詳しくは知りませんの。
  • ということでC++の標準規格に従ってみよう。
  • ISO/IEC 14882の規格を見てみると
5.3.5 - Delete [expr.delete]
2 (中略)In either alternative, if the value of the operand of delete is the null pointer the operation has no effect. 
  • とある。英語が不得意なことを忘れていたので改めて、JISX3014でこの項目を見てみると
5.3.5 delete式
2 (中略)いずれの形式であっても, deleteの演算対象の値が空のポインタの場合, その演算の効果はない.
  • とのこと。つまり、NULLポインタをdeleteしても何も起こりませんよ、と規格で保障されている。
  • ということは、safe_deleteにはif(p != NULL)の処理はなくてもかまわないと思える。
  • ただ、COMで使われる参照カウンタのReleaseの場合、話は別。あれはMSの方針に従うべき。

備考

  • templateを使うと、型の数だけ関数が作られて実行ファイルが大きくなっちゃうよね。
    • オーベーヘッドになりかねないから、defineマクロのほうがよくない?って意見も考えられなくはない。
  • 必ずしも、inline関数がインライン化されるとは限らない。コンパイラによる。
  • inline関数はちょっとした定義のものじゃないとインライン化されない。
  • #defineマクロはプリプロセッサ命令、コンパイル前の段階で展開され置き換えがおきる。
  • inline関数だと、コンパイル段階で展開され置き換えられる。
  • NULL使ってる時点で#define廃止できてないよね。
    • 可読性のためにNULL使ってるだけで、実際は、0とかに置き換えたほうがいいかと思われ。
  • SAFE_DELETE_ARRAY()みたく配列を解放するときの使い分けが面倒。
    • <boost/array.hpp>を使えばおk。boost::shared_arrayとか便利よ。
    • もしくはSTLとか。vectorさんなら配列みたく使えるぴょん。
  • いっそスマートポインタ使えばよくない?
    • うん、エレガントなコードに期待してるよ。


参考文献

記事メニュー
目安箱バナー