C++のnew/deleteによる動的メモリ管理を完全解説!ポインタの基本から応用まで
生徒
「C++でメモリを『自由に確保する』っていう話を聞いたんですが、どういうことですか?」
先生
「それは『動的メモリ管理』のことですね。プログラムを動かしている最中に、必要な分だけメモリを借りる方法です。」
生徒
「あらかじめ決めておかなくてもいいんですか?なんだか魔法みたいですね!」
先生
「そうなんです。でも、借りたものは返さないといけない、というルールがあるんですよ。今日はそのための命令である『new』と『delete』について詳しく解説します!」
1. 動的メモリ管理とは?
C++のプログラムにおいて、データを保存するための場所をメモリと呼びます。通常、変数を宣言すると自動的にメモリが割り当てられますが、これには「プログラムを作る時点でサイズが決まっていなければならない」という制限があります。
しかし、実際にソフトを使ってみると「入力されるデータの数がその時々で違う」という場面がよくあります。例えば、SNSの投稿一覧を表示するとき、投稿が10件なのか1000件なのかは動かしてみないと分かりませんよね。このように、プログラムの実行中に必要な分だけメモリを確保することを「動的メモリ管理」と呼びます。これにより、無駄なく効率的にパソコンの資源を使うことができるようになります。
2. メモリを借りる魔法の言葉「new」
C++で動的にメモリを確保するには、newという演算子を使います。これはパソコンに対して「今から特定の型のデータを置くための場所を貸して!」とお願いする命令です。
newを使うと、パソコンはメモリの中から空いている場所を探し、その場所の住所(ポインタ)を教えてくれます。私たちはその住所をポインタ変数に保存して、データにアクセスすることになります。例えるなら、ホテルのフロントで部屋を借りて、その「部屋番号(住所)」が書かれたカードキーを受け取るようなイメージです。
3. 実際にnewを使ってみよう
まずは、整数(int型)のメモリを一つだけ動的に確保する簡単なプログラムを見てみましょう。ポインタを使って、そのメモリに値を書き込んだり、読み出したりします。
#include <iostream>
int main() {
// 1. 整数1個分のメモリを確保して、その住所を ptr に入れる
int* ptr = new int;
// 2. その場所に 100 という値を書き込む(*ptr は「住所の中身」という意味)
*ptr = 100;
// 3. 画面に表示して確認
std::cout << "確保した場所の値: " << *ptr << std::endl;
// 注意:この後、必ず delete が必要ですが、ここではまず new の動きを確認しました
delete ptr;
return 0;
}
確保した場所の値: 100
4. 借りたメモリを返す「delete」
ここが最も重要なポイントです。newで借りたメモリは、使い終わったら必ず delete で返さなければなりません。
もし返却を忘れてしまうと、パソコンのメモリが「使用中」のままになり続け、最終的にはパソコン全体の動作が重くなってしまいます。これをメモリリークと呼びます。ホテルの部屋を借りっぱなしにしてチェックアウトしないと、次のお客さんが泊まれなくなってしまうのと同じです。自分のプログラムが責任を持って、後片付けをする必要があるのです。
5. 配列をまとめて確保する
一つのデータだけでなく、複数のデータをまとめて確保することもできます。これを「動的配列」と呼びます。確保するときは new[]、返すときは delete[] という少し特殊な形を使います。これは、パソコンが「これは一つの部屋じゃなくて、連続した複数の部屋だな」と判断するために必要な書き方です。
#include <iostream>
int main() {
int size;
std::cout << "作成する配列のサイズを入力してください: ";
std::cin >> size;
// ユーザーが入力した数だけ、整数の箱を用意する
int* array = new int[size];
// 配列にデータを入れてみる
for (int i = 0; i < size; i++) {
array[i] = i * 10;
std::cout << i << "番目のデータ: " << array[i] << std::endl;
}
// 使い終わったので配列を丸ごと返却する
delete[] array;
return 0;
}
作成する配列のサイズを入力してください: 3
0番目のデータ: 0
1番目のデータ: 10
2番目のデータ: 20
6. ポインタとメモリのイメージ図
プログラミング未経験の方にとって、ポインタは非常に掴みどころがない概念かもしれません。イメージとしては、以下のようになります。
- メモリ本体: 巨大な図書館。本棚がたくさんある。
- new: 図書館の受付で「1冊分の棚を自分専用に予約する」こと。
- ポインタ変数: 「予約した棚がどこにあるか」を書いた付箋(ふせん)。
- *(アスタリスク): 付箋の情報を頼りに、実際にその棚まで歩いていって中身を触ること。
- delete: 受付に行って「予約をキャンセルして棚を解放する」こと。
もし付箋を捨ててしまったのに予約をキャンセル(delete)し忘れると、誰もその棚を使えない「開かずの棚」ができてしまいます。これがメモリ管理の失敗です。
7. 安全なメモリ管理のための注意点
動的メモリ管理を行う上で、絶対にやってはいけない「危険な操作」がいくつかあります。初心者のうちは、以下の2点に特に気をつけましょう。
- 二重解放(ダブルフリー): 同じ住所に対して
deleteを2回実行すること。これはパソコンを混乱させ、プログラムがクラッシュ(強制終了)する原因になります。 - 解放後のアクセス:
deleteした後の住所をまだ使えると思い込んで読み書きすること。すでに他の人が使っているかもしれない場所を勝手にいじるのは非常に危険です。
対策として、delete した直後のポインタ変数には nullptr(ヌルポインタ)という「どこも指していない」という印を代入しておくのが、プロの間でもよく使われるテクニックです。
int* data = new int(50);
delete data; // 返却
data = nullptr; // 安全のために「空っぽ」にする
8. ヒープ領域とスタック領域の違い
少し専門的な話になりますが、メモリには大きく分けて2つのエリアがあります。普通の変数が置かれるスタック領域と、newで借りるヒープ領域です。
スタック領域は、決まった手順で自動的に片付けられる「使い勝手の良い小さな机」のようなものです。一方でヒープ領域は、広大で自由に使えるけれど、自分で片付けをしなければならない「倉庫」のような場所です。大きなデータや、プログラムのあちこちで使い回したいデータは、このヒープ領域に new を使って配置するのがC++の基本スタイルです。
9. スマートポインタへの第一歩
現代のC++では、実は new や delete を直接書くことは減っています。というのも、さらに進んだ「スマートポインタ」という仕組みを使えば、返却を自動化できるからです。しかし、その便利な仕組みの裏側では、必ず今回学んだ new と delete が動いています。基本を理解しているからこそ、応用的なテクニックも正しく使いこなせるようになるのです。
メモリ管理をマスターすることは、パソコンの性能を限界まで引き出す「C++の達人」への大きな一歩です。最初は難しく感じるかもしれませんが、自分でメモリを操れるようになると、プログラミングの世界がグッと広がりますよ!