ぼく用あれこれまとめ
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