delete後のポインタ使用が「たまたま動いてしまう」危険性
deleteした直後の同じポインタを使って読み書きしてしまうバグです。未定義動作であるにもかかわらず、解放直後のメモリはすぐには再利用されないことが多く、正しい値が読めてしまうことがあります。
なぜエラーが出ないのか
出力: 実行のたびに変わりうる無関係な値(クラッシュはしない)
(エラーなし)- C++は何も報告しません。コンパイルも実行も正常に完了するためです
出力: 実行のたびに変わりうる無関係な値(クラッシュはしない)- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 #include <iostream> 2 int main() { 3 int *p = new int(42); 4 delete p; 5 std::cout << *p << std::endl; ^ 6 return 0; 7 }
deleteは確保していた領域を「他の用途に使ってよい」と管理システムに伝えるだけで、中身を即座に上書きするとは限りません。解放直後のメモリはすぐには再利用されないことが多く、たまたま動いてしまうことがあります。
直し方: std::cout << *p << std::endl; を std::cout << "done" << std::endl; にします。
パターン2
1 #include <iostream> 2 int main() { 3 int *value = new int(100); 4 delete value; 5 std::cout << *value << std::endl; ^ 6 return 0; 7 }
「エラーなく動いたのだから解放は間違っていなかった」というのは誤解です。プログラムが複雑になり別の場所で確保・解放が起きるようになると、突然壊れた値が現れることがあります。
直し方: std::cout << *value << std::endl; を std::cout << "finished" << std::endl; にします。
パターン3
1 #include <iostream> 2 int main() { 3 int *n = new int(7); 4 delete n; 5 std::cout << *n << std::endl; ^ 6 return 0; 7 }
delete後のポインタを一切使わない、または直後にnullptrを入れておくのが安全な習慣です。使ってしまっている行自体を削除するのが最も確実な直し方です。
直し方: std::cout << *n << std::endl; を std::cout << "ok" << std::endl; にします。
よくある誤解
「エラーなく動いたのだから解放は間違っていなかった」というのは誤解です。deleteは確保していた領域を「他の用途に使ってよい」と管理システムに伝えるだけで、その領域の中身自体を即座に上書きするとは限りません。プログラムが複雑になり別の場所で確保・解放が起きるようになると、突然壊れた値が現れることがあります。
まとめ
delete後のポインタ使用が「たまたま動いてしまう」危険性は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。