16_メモリの動的確保

はじめに

この章では、C++における「動的なメモリ確保と解放」について学びます。プログラムの実行中に必要なだけメモリを確保したり、不要になったら解放することで、柔軟なメモリ管理が可能になります。

静的メモリ確保 と 動的メモリ確保

種類 宣言例 確保のタイミング 解放方法
静的確保 int a; コンパイル時 自動
動的確保 int* p = new int; 実行時(プログラム中) delete p;

単一の変数を動的に確保

// 16_new_delete_basic.cpp
#include <iostream>
using namespace std;

int main() {
    int* p = new int;  // int型のメモリを1つ確保
    *p = 100;          // 値を代入
    cout << *p << endl; // 値を表示
    delete p;          // メモリを解放
    return 0;
}

配列を動的に確保

// 16_new_delete_array.cpp
#include <iostream>
using namespace std;

int main() {
    int size;
    cout << "配列のサイズを入力: ";
    cin >> size;

    int* arr = new int[size];
    for (int i = 0; i < size; i++) arr[i] = i * 10;

    for (int i = 0; i < size; i++) cout << arr[i] << " ";
    cout << endl;

    delete[] arr;  // 配列は delete[] で解放
    return 0;
}
注意: 配列を動的確保した場合は delete ではなく必ず delete[] を使ってください。間違えるとメモリリークや未定義動作の原因になります。

vectorとの違い

項目 new / delete vector
サイズの可変性 自分で確保し直す必要あり .push_back()で簡単に拡張可能
解放 明示的に delete 必要 自動で解放される
安全性 ミスするとメモリリーク 安全で例外処理も対応
推奨度 低(必要なときのみ) 高(基本的にこちらを使う)
通常は vector を使えば十分。new は特別な場合にだけ使う。

理解度チェック

問題16-1

次のコードの出力は何ですか?

int* p = new int[3];
p[0] = 10; p[1] = 20; p[2] = 30;
cout << p[1];
delete[] p;
解説を表示 正解:イ
new int[3] で3要素の配列を動的確保し、p[0]=10, p[1]=20, p[2]=30 と代入。p[1] は 20 です。動的確保した配列は通常の配列と同じ添字でアクセスできます。

問題16-2

動的確保した配列の解放に正しい構文はどれですか?

解説を表示 正解:ウ
new[] で確保した配列は必ず delete[] で解放します。delete(角括弧なし)は単一オブジェクト用です。free() はC言語の関数でC++の new には使えません。

問題16-3

次のコードにはどんな問題がありますか?

int* p = new int[5];
for (int i = 0; i < 5; i++) p[i] = i;
// delete[] を忘れた
return 0;
解説を表示 正解:ア
new で確保したメモリは delete しない限り解放されません。プログラム終了時にOSが回収することもありますが、長時間動作するプログラムや繰り返し確保するループでは深刻なメモリリークになります。

問題16-4

動的確保と vector の比較として正しい説明はどれですか?

解説を表示 正解:イ
vector はスコープを抜けると自動でメモリを解放(デストラクタが呼ばれる)するため、メモリリークのリスクがありません。要素数も実行時に push_back() で動的に追加できます。

ポインタ経由のメンバアクセス(->演算子)

new で生成したオブジェクトはポインタで受け取ります。 ポインタ経由でメンバ変数・メンバ関数にアクセスするときは、 ドット(.)の代わりに ->(アロー演算子) を使います。

書き方使う場面
obj.method()通常のオブジェクト(スタック上)enemy.attack();
ptr->method()ポインタ(newで確保したオブジェクト)enemy->attack();
// 例:->演算子の使い方
#include <iostream>
using namespace std;

class Enemy {
public:
    string name;
    void attack() {
        cout << name << " が攻撃した!" << endl;
    }
};

int main() {
    Enemy obj;           // スタック上のオブジェクト
    obj.name = "スライム";
    obj.attack();        // . でアクセス

    Enemy* ptr = new Enemy();  // ヒープ上のオブジェクト
    ptr->name = "ゴブリン";
    ptr->attack();            // -> でアクセス
    delete ptr;
    return 0;
}
覚え方: 自分で new して作ったオブジェクトには ->、普通に宣言したオブジェクトには . を使う。

コーディング演習

演習1:基本コードを動かす

16_01_dynmem.cpp を作成し、次のコードを入力して実行しましょう。
// main.cpp
#include <iostream>
using namespace std;

int main() {
    int size;
    cout << "配列のサイズを入力: ";
    cin >> size;

    int* arr = new int[size];
    for (int i = 0; i < size; i++) arr[i] = i * 10;

    for (int i = 0; i < size; i++) cout << arr[i] << " ";
    cout << endl;

    delete[] arr;
    return 0;
}

演習2:コードを改造する

16_01_DynMemmain.cpp をコピーして 16_02_DynMemMod に貼り付け、次の変更を加えてみましょう:
解答例を表示
// main.cpp
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "人数を入力: ";
    cin >> n;

    int* scores = new int[n];
    int total = 0;

    for (int i = 0; i < n; i++) {
        cout << i + 1 << "人目の点数: ";
        cin >> scores[i];
        total += scores[i];
    }

    cout << "合計: " << total << endl;
    cout << "平均: " << (double)total / n << endl;

    delete[] scores;
    return 0;
}

まとめ