new[]とdeleteの不一致が「たまたま動いてしまう」危険性
new[]で確保した配列に対して、delete[]ではなく単体用のdeleteを使ってしまう不一致です。未定義動作であるにもかかわらず、int型のような単純な配列ではクラッシュせずに動いてしまうことがあります。
なぜエラーが出ないのか
出力: done(クラッシュせず動いてしまう)
(エラーなし)- C++は何も報告しません。コンパイルも実行も正常に完了するためです
出力: done(クラッシュせず動いてしまう)- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 #include <iostream> 2 int main() { 3 int *arr = new int[5]; 4 delete arr; ^ 5 std::cout << "done" << std::endl; 6 return 0; 7 }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない
new[]で確保した配列にはdelete[]を使うのが正しい対応です。単体用のdeleteを使うのは未定義動作ですが、int型のような単純な配列ではクラッシュせずに動いてしまうことがあります。動いたからといって正しいわけではありません。
直し方: delete arr; を delete[] arr; にします。
広告
広告スロット(未設定)
パターン2
1 #include <iostream> 2 int main() { 3 double *vals = new double[10]; 4 delete vals; ^ 5 std::cout << "done" << std::endl; 6 return 0; 7 }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない
「クラッシュしないなら問題ない」というのは危険な誤解です。オブジェクトの配列で同じミスをすると、配列内の最初の要素以外のデストラクタが呼ばれないなど、より深刻な形で表面化することがあります。
直し方: delete vals; を delete[] vals; にします。
パターン3
1 #include <iostream> 2 int main() { 3 char *buf = new char[64]; 4 delete buf; ^ 5 std::cout << "done" << std::endl; 6 return 0; 7 }
出力: done(クラッシュせず動いてしまう)危険性: 未定義動作であり保証された動作ではない
new[]とdeleteの対応、およびnewとdelete[]の対応、どちらの組み合わせ違いも未定義動作です。確保に使った形と解放に使う形は必ず揃える必要があります。
直し方: delete buf; を delete[] buf; にします。
よくある誤解
「クラッシュしないなら問題ない」というのは危険な誤解です。この不一致は未定義動作であり、動くかどうかは処理系や型(デストラクタの有無など)に依存します。オブジェクトの配列で同じミスをすると、配列内の最初の要素以外のデストラクタが呼ばれないなど、より深刻な形で表面化することがあります。
まとめ
new[]とdeleteの不一致が「たまたま動いてしまう」危険性は上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。