ぼく用あれこれまとめ
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さんなら配列みたく使えるぴょん。
- いっそスマートポインタ使えばよくない?
- うん、エレガントなコードに期待してるよ。
参考文献
- MSDN - C/C++ Preprocessor Reference(英語)
- ISO/IEC 14882:1998
- http://www.kuzbass.ru:8086/docs/isocpp/ - C++の規格書
- ISO/IEC 14882:2003
- 日本工業規格JIS X 3014:2003
- http://www.jisc.go.jp/でJIS番号"X3014"でデータベース検索
- 個人的な話、chromeだとうまく表示ができなかった。IEだとうまく見れた。
- 日本工業規格JIS X 3014:2003