「SAFE_DELETE()系のdefineマクロ廃止」の編集履歴(バックアップ)一覧はこちら
「SAFE_DELETE()系のdefineマクロ廃止」(2011/03/31 (木) 09:07:56) の最新版変更点
追加された行は緑色になります。
削除された行は赤色になります。
*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*&)のように、引数は「&bold(){ポインタの参照}」を使うのです。
-深く考えずに、こう考えるのが早い?
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>http://msdn.microsoft.com/ja-jp/library/y4skk93w.aspx]](英語)
-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だとうまく見れた。
*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*&)のように、引数は「&bold(){ポインタの参照}」を使うのです。
-深く考えずに、こう考えるのが早い?
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>http://msdn.microsoft.com/ja-jp/library/y4skk93w.aspx]](英語)
-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だとうまく見れた。
表示オプション
横に並べて表示:
変化行の前後のみ表示: