
C++入門|new演算子とdelete演算子
newはヒープ領域に修行カプセルを召喚する合図、deleteは使い終えたカプセルを消去する後片付け。C++では、生成したメモリは自分の手で責任を持って解放します。
C++では、クラスからインスタンスを生成するとき、これまで次のような書き方をしてきました。
KiTrainingCapsule capsule;このように関数の中で普通に宣言したインスタンスは、主にスタック領域に作られます。
スタック領域に作られたインスタンスは、その関数の処理が終わると自動的に消えます。
つまり、main関数の中で作ったインスタンスなら、main関数が終わると自動的に消去されます。
これはとても便利ですが、いつ生成して、いつ消すかを細かく自分で決めたい場面もあります。
たとえば、ドラゴンボール風に考えてみましょう。
近未来の修行司令室で、必要なタイミングだけ重力修行カプセルを起動したいとします。
修行が終わったら、main関数の終了を待たずに、すぐにそのカプセルを消したい場合もあります。
このように、自由なタイミングでインスタンスを生成し、自由なタイミングで消去したいときに使うのが、new演算子とdelete演算子です。
| 演算子 | 役割 | ドラゴンボール風のイメージ |
|---|---|---|
| new | ヒープ領域にインスタンスを生成する | 修行カプセルを任意のタイミングで召喚する |
| delete | newで生成したインスタンスを消去する | 使い終えた修行カプセルを片付ける |
newで作ったインスタンスは、ヒープ領域に作られます。
ヒープ領域に作られたインスタンスは、スタック領域のように関数終了で自動的に消えるとは限りません。
そのため、使い終わったらdeleteで消去する必要があります。
KiTrainingCapsule* pCapsule = new KiTrainingCapsule();
delete pCapsule;この流れを理解すると、C++でメモリを扱う感覚がぐっと深まります。
図:newでヒープ領域に修行カプセルを生成する

この図が示していること
この図では、new演算子によってヒープ領域にKiTrainingCapsuleインスタンスが生成され、そのアドレスをポインタ変数pCapsuleが記憶する流れを表しています。
pCapsuleそのものはmain.cppの中で使うポインタ変数です。
実際のインスタンスはヒープ領域に作られます。
使い終わったら、delete pCapsuleでヒープ領域のインスタンスを消去します。
メモリの4領域
C++のプログラムを実行すると、OSはそのプログラムのためにメモリ領域を用意します。
このメモリ領域は、大きく次の4つに分けて考えることができます。
| 領域 | 置かれるもの | イメージ |
|---|---|---|
| プログラム領域 | 実行されるプログラム本体 | 修行司令書そのもの |
| 静的領域 | グローバル変数、static変数 | 最初から最後まで残る固定設備 |
| ヒープ領域 | newで動的に確保したメモリ | 必要なときに出現させる修行カプセル置き場 |
| スタック領域 | ローカル変数など | 関数の中だけ使う一時的な作業台 |
ドラゴンボール風にたとえると、プログラム領域は修行司令室に登録された命令書です。
静的領域は、プログラム開始から終了までずっと存在する固定設備です。
スタック領域は、関数が動いている間だけ使う一時的な作業台です。
そしてヒープ領域は、必要なタイミングで修行カプセルを出したり消したりできる自由な空間です。

スタック領域に作られるインスタンス
これまでよく使ってきたインスタンス生成は、次のような形でした。
KiTrainingCapsule capsule;このように関数の中で普通に宣言したインスタンスは、スタック領域に作られます。
スタック領域にあるインスタンスは、その関数の処理が終わると自動的に消えます。
たとえば、main関数の中でcapsuleを作った場合、main関数が終わるときにcapsuleも消えます。
int main(int argc, char** argv) {
KiTrainingCapsule capsule;
capsule.setPower(400);
capsule.train(1.5);
return 0;
}この場合、capsuleはmain関数の終了時に自動的に消去されます。
その直前にデストラクタが呼び出されます。
つまり、スタック領域のインスタンスは、生成と消去のタイミングが関数の範囲に強く結びついています。
ヒープ領域に作られるインスタンス
一方、new演算子を使うと、インスタンスをヒープ領域に作れます。
KiTrainingCapsule* pCapsule = new KiTrainingCapsule();この書き方では、KiTrainingCapsuleインスタンスがヒープ領域に生成されます。
そして、そのインスタンスのアドレスがpCapsuleに入ります。
pCapsuleはポインタ変数です。
| 書き方 | 意味 |
|---|---|
| KiTrainingCapsule* | KiTrainingCapsule型のインスタンスを指すポインタ |
| pCapsule | ポインタ変数名 |
| new KiTrainingCapsule() | ヒープ領域にKiTrainingCapsuleインスタンスを生成する |
| pCapsule = new KiTrainingCapsule(); | 生成したインスタンスのアドレスをpCapsuleに入れる |
ヒープ領域に作ったインスタンスは、deleteで消去するまで残ります。
delete pCapsule;このdeleteを忘れると、使い終わったメモリが解放されずに残ってしまいます。
C++では、newで生成したものは、基本的にdeleteで消す必要があります。
スタックとヒープの違い
スタック領域とヒープ領域の違いを整理すると、次のようになります。
| 比較項目 | スタック領域 | ヒープ領域 |
|---|---|---|
| 生成方法 | KiTrainingCapsule capsule; | new KiTrainingCapsule() |
| 消去方法 | 関数終了時に自動で消える | deleteで明示的に消す |
| 扱い方 | 変数名で直接使う | ポインタを通して使う |
| メンバアクセス | capsule.train(1.5); | pCapsule->train(1.5); |
| 向いている場面 | 短い範囲で使う一時的なインスタンス | 自由なタイミングで生成・消去したいインスタンス |
ドラゴンボール風に言えば、スタック領域は「関数内で一時的に使う訓練台」です。
ヒープ領域は「必要なときに呼び出して、使い終わったら自分で片付ける修行カプセル置き場」です。
図:スタック領域とヒープ領域の違い

この図が示していること
この図では、スタック領域とヒープ領域の違いを表しています。
スタック領域に作ったインスタンスは、関数が終わると自動的に消えます。
ヒープ領域に作ったインスタンスは、newで生成し、deleteで消去します。
また、スタック上のインスタンスはドット演算子でメンバへアクセスします。
ヒープ上のインスタンスをポインタで扱う場合は、アロー演算子を使います。
今回作成するサンプルプログラム
今回のサンプルでは、KiTrainingCapsuleクラスを使います。
KiTrainingCapsuleは、修行カプセルを表すクラスです。
コンストラクタでは起動メッセージを表示し、デストラクタでは終了メッセージを表示します。
newでインスタンスを生成したタイミングで、コンストラクタが呼び出されます。
deleteでインスタンスを消去したタイミングで、デストラクタが呼び出されます。
| メンバ | 内容 |
|---|---|
| KiTrainingCapsule | コンストラクタ。修行カプセル起動時の処理 |
| ~KiTrainingCapsule | デストラクタ。修行カプセル終了時の処理 |
| setPower | 気の出力を設定する |
| getTotalEnergy | 総蓄積エネルギーを取得する |
| train | 指定した時間だけ修行する |
ファイル構成は、次の3つです。
| ファイル | 役割 |
|---|---|
| ki_training_capsule.h | KiTrainingCapsuleクラスの宣言 |
| ki_training_capsule.cpp | コンストラクタ、デストラクタ、メンバ関数の定義 |
| main.cpp | newとdeleteを使ってインスタンスを生成・消去する |
KiTrainingCapsuleクラスの宣言
プロジェクト/ファイル名: Chap4_02/ki_training_capsule.h
#ifndef _KI_TRAINING_CAPSULE_H_
#define _KI_TRAINING_CAPSULE_H_
class KiTrainingCapsule {
public:
// コンストラクタ
KiTrainingCapsule();
// デストラクタ
~KiTrainingCapsule();
// 気の出力を設定する
void setPower(double power);
// 気の出力を取得する
double getPower();
// 総蓄積エネルギーを取得する
double getTotalEnergy();
// 指定した時間だけ修行する
void train(double hour);
private:
// 現在の気の出力
double m_power;
// 総蓄積エネルギー
double m_totalEnergy;
};
#endif // _KI_TRAINING_CAPSULE_H_このヘッダーファイルでは、KiTrainingCapsuleクラスを宣言しています。
注目したいのは、コンストラクタとデストラクタです。
KiTrainingCapsule();
~KiTrainingCapsule();KiTrainingCapsule()は、インスタンスが生成されたときに呼び出されます。
~KiTrainingCapsule()は、インスタンスが消去される直前に呼び出されます。
newでインスタンスを生成したときにも、コンストラクタは呼び出されます。
deleteでインスタンスを消去したときにも、デストラクタは呼び出されます。
KiTrainingCapsuleクラスの定義
プロジェクト/ファイル名: Chap4_02/ki_training_capsule.cpp
#include "ki_training_capsule.h"
#include <iostream>
using namespace std;
// コンストラクタ
KiTrainingCapsule::KiTrainingCapsule() : m_power(0.0), m_totalEnergy(0.0) {
cout << "修行カプセルをヒープ領域に起動しました。" << endl;
}
// デストラクタ
KiTrainingCapsule::~KiTrainingCapsule() {
cout << "修行カプセルをヒープ領域から消去しました。" << endl;
}
// 気の出力を設定する
void KiTrainingCapsule::setPower(double power) {
m_power = power;
}
// 気の出力を取得する
double KiTrainingCapsule::getPower() {
return m_power;
}
// 総蓄積エネルギーを取得する
double KiTrainingCapsule::getTotalEnergy() {
return m_totalEnergy;
}
// 指定した時間だけ修行する
void KiTrainingCapsule::train(double hour) {
cout << "気の出力" << m_power << "で" << hour << "時間修行" << endl;
cout << m_power * hour << "ポイントの気を蓄積しました。" << endl;
// 修行で蓄積した気を総蓄積エネルギーに加算する
m_totalEnergy += m_power * hour;
}このファイルでは、KiTrainingCapsuleクラスのメンバ関数を定義しています。
コンストラクタでは、m_powerとm_totalEnergyを0.0で初期化しています。
KiTrainingCapsule::KiTrainingCapsule() : m_power(0.0), m_totalEnergy(0.0) {
cout << "修行カプセルをヒープ領域に起動しました。" << endl;
}new KiTrainingCapsule()が実行されると、このコンストラクタが呼び出されます。
デストラクタでは、消去メッセージを表示しています。
KiTrainingCapsule::~KiTrainingCapsule() {
cout << "修行カプセルをヒープ領域から消去しました。" << endl;
}delete pCapsuleが実行されると、このデストラクタが呼び出されます。
newとdeleteを使うmain.cpp
プロジェクト/ファイル名: Chap4_02/main.cpp
#include <iostream>
#include "ki_training_capsule.h"
using namespace std;
int main(int argc, char** argv) {
// 修行カプセルを指すポインタ変数を用意する
KiTrainingCapsule* pCapsule = NULL;
// ヒープ領域に修行カプセルのインスタンスを生成する
pCapsule = new KiTrainingCapsule();
// 気の出力を設定する
pCapsule->setPower(400);
// 指定した時間だけ修行する
pCapsule->train(1.5);
// 気の出力を再設定する
pCapsule->setPower(650);
// 指定した時間だけ修行する
pCapsule->train(2.0);
// 総蓄積エネルギーを表示する
cout << "総蓄積エネルギー:" << pCapsule->getTotalEnergy() << "ポイント" << endl;
// ヒープ領域のインスタンスを消去する
delete pCapsule;
// 消去が終わったことを表示する
cout << "修行カプセルの消去処理が終了しました。" << endl;
// プログラムが正常に終わったことを示す
return 0;
}実行結果は次のようになります。
修行カプセルをヒープ領域に起動しました。
気の出力400で1.5時間修行
600ポイントの気を蓄積しました。
気の出力650で2時間修行
1300ポイントの気を蓄積しました。
総蓄積エネルギー:1900ポイント
修行カプセルをヒープ領域から消去しました。
修行カプセルの消去処理が終了しました。この実行結果を見ると、newとdeleteのタイミングが分かります。
最初の次の表示は、newでインスタンスを生成したときに呼び出されたコンストラクタによるものです。
修行カプセルをヒープ領域に起動しました。そして、次の表示は、deleteでインスタンスを消去したときに呼び出されたデストラクタによるものです。
修行カプセルをヒープ領域から消去しました。main関数の終了を待たずに、delete pCapsuleの位置でインスタンスを消去できていることが分かります。
ポインタ変数をNULLで初期化する
main.cppの最初では、ポインタ変数をNULLで初期化しています。
KiTrainingCapsule* pCapsule = NULL;pCapsuleは、KiTrainingCapsuleインスタンスのアドレスを記憶するためのポインタ変数です。
まだインスタンスを指していない段階なので、NULLを入れています。
| 書き方 | 意味 |
|---|---|
| KiTrainingCapsule* | KiTrainingCapsule型を指すポインタ |
| pCapsule | ポインタ変数名 |
| NULL | まだ何も指していない状態 |
ドラゴンボール風に言えば、pCapsuleは修行カプセルの位置を記録する司令室のレーダーです。
最初はまだカプセルを召喚していないので、何も指していない状態です。
newを実行すると、ヒープ領域にカプセルが出現し、その場所をpCapsuleが記憶します。
new演算子でインスタンスを生成する
次の行で、new演算子を使っています。
pCapsule = new KiTrainingCapsule();この処理では、ヒープ領域にKiTrainingCapsuleインスタンスを生成しています。
そして、生成されたインスタンスのアドレスがpCapsuleに代入されます。
このタイミングで、KiTrainingCapsuleクラスのコンストラクタが呼び出されます。
修行カプセルをヒープ領域に起動しました。newの基本的な書式は、次のようになります。
new コンストラクタ名()今回の場合は、次のようになります。
new KiTrainingCapsule()KiTrainingCapsule()はコンストラクタです。
newは、そのコンストラクタを呼び出して、ヒープ領域にインスタンスを生成します。
ポインタからメンバへアクセスする
newで生成したインスタンスは、ポインタ変数pCapsuleを通して操作します。
ポインタ変数からメンバ関数を呼び出すときは、アロー演算子を使います。
pCapsule->setPower(400);
pCapsule->train(1.5);
pCapsule->getTotalEnergy();スタック領域に作ったインスタンスでは、ドット演算子を使いました。
capsule.setPower(400);しかし、ポインタ変数の場合はドット演算子ではなく、アロー演算子を使います。
| 対象 | 書き方 | 例 |
|---|---|---|
| 通常のインスタンス | . | capsule.setPower(400); |
| ポインタ変数 | -> | pCapsule->setPower(400); |
pCapsuleはインスタンスそのものではなく、インスタンスのアドレスを持っています。
そのため、pCapsuleが指している先のインスタンスのメンバを使う、という意味でアロー演算子を使います。
delete演算子でインスタンスを消去する
修行が終わったら、delete演算子でインスタンスを消去します。
delete pCapsule;この処理によって、newでヒープ領域に生成したKiTrainingCapsuleインスタンスが消去されます。
このタイミングで、デストラクタが呼び出されます。
修行カプセルをヒープ領域から消去しました。deleteの基本的な書式は、次のようになります。
delete ポインタ変数今回の場合は、次のようになります。
delete pCapsule;newで生成したインスタンスは、自動では消えません。
そのため、使い終わったら必ずdeleteで消去します。
ドラゴンボール風に言えば、修行カプセルをヒープ領域に召喚したら、修行終了後にきちんと格納庫へ戻す必要があるということです。
newとdeleteの処理の流れ
今回の処理の流れを整理すると、次のようになります。
| 手順 | 処理 | 内容 |
|---|---|---|
| 1 | KiTrainingCapsule* pCapsule = NULL; | ポインタ変数を用意する |
| 2 | pCapsule = new KiTrainingCapsule(); | ヒープ領域にインスタンスを生成する |
| 3 | コンストラクタ実行 | m_powerとm_totalEnergyを0.0に初期化する |
| 4 | pCapsule->setPower(400); | 気の出力を400に設定する |
| 5 | pCapsule->train(1.5); | 600ポイント蓄積する |
| 6 | pCapsule->setPower(650); | 気の出力を650に設定する |
| 7 | pCapsule->train(2.0); | 1300ポイント蓄積する |
| 8 | pCapsule->getTotalEnergy(); | 1900ポイントを取得する |
| 9 | delete pCapsule; | インスタンスを消去する |
| 10 | デストラクタ実行 | 終了処理が行われる |
newで生成したタイミングでコンストラクタが呼び出されます。
deleteで消去したタイミングでデストラクタが呼び出されます。
図:newからdeleteまでの流れ

この図が示していること
この図では、new演算子でインスタンスを生成し、ポインタ変数pCapsuleを使って操作し、最後にdelete演算子で消去する流れを表しています。
new KiTrainingCapsule()でヒープ領域に修行カプセルが生成されます。
pCapsule->setPowerやpCapsule->trainで、その修行カプセルを操作します。
delete pCapsuleで修行カプセルが消去され、デストラクタが呼び出されます。
ドット演算子とアロー演算子の違い
C++では、インスタンスを直接扱う場合と、ポインタで扱う場合で、メンバへのアクセス方法が変わります。
スタック領域のインスタンスを直接使う場合は、ドット演算子を使います。
KiTrainingCapsule capsule;
capsule.setPower(400);
capsule.train(1.5);一方、newで生成したインスタンスをポインタで扱う場合は、アロー演算子を使います。
KiTrainingCapsule* pCapsule = new KiTrainingCapsule();
pCapsule->setPower(400);
pCapsule->train(1.5);
delete pCapsule;表にすると、次のようになります。
| 使い方 | インスタンス生成 | メンバアクセス |
|---|---|---|
| 直接扱う | KiTrainingCapsule capsule; | capsule.setPower(400); |
| ポインタで扱う | KiTrainingCapsule* pCapsule = new KiTrainingCapsule(); | pCapsule->setPower(400); |
アロー演算子は、ポインタが指している先のインスタンスにアクセスするための記号です。
main関数の終了前に消去できる
スタック領域に作ったインスタンスは、関数が終わるときに自動的に消えます。
一方、newで作ったインスタンスは、deleteを書いたタイミングで消せます。
今回のサンプルでは、次の位置でインスタンスを消去しています。
delete pCapsule;
cout << "修行カプセルの消去処理が終了しました。" << endl;実行結果を見ると、delete pCapsuleのタイミングでデストラクタのメッセージが表示され、そのあとに消去完了メッセージが表示されます。
修行カプセルをヒープ領域から消去しました。
修行カプセルの消去処理が終了しました。つまり、main関数が完全に終了する前に、ヒープ領域のインスタンスを消去できています。
これは、newとdeleteを使う大きな特徴です。
newで作ったらdeleteする
C++では、newで生成したインスタンスは、使い終わったらdeleteで消去する必要があります。
KiTrainingCapsule* pCapsule = new KiTrainingCapsule();
// インスタンスを使う処理
delete pCapsule;newだけを書いてdeleteを書かないと、ヒープ領域に確保したメモリが残り続けてしまいます。
これは、修行が終わったのに修行カプセルを格納庫へ戻さず、ヒープ領域に置きっぱなしにするようなものです。
プログラムが大きくなると、こうした消し忘れが問題になります。
そのため、newとdeleteはセットで考えることが大切です。
| 操作 | 役割 |
|---|---|
| new | ヒープ領域にインスタンスを生成する |
| delete | newで生成したインスタンスを消去する |
new演算子とdelete演算子で押さえたいポイント
new演算子とdelete演算子では、次のポイントを押さえておきましょう。
| ポイント | 内容 |
|---|---|
| new | ヒープ領域にインスタンスを生成する |
| delete | newで生成したインスタンスを消去する |
| new実行時 | コンストラクタが呼び出される |
| delete実行時 | デストラクタが呼び出される |
| ポインタ変数 | newで生成したインスタンスのアドレスを記憶する |
| アロー演算子 | ポインタからメンバへアクセスする |
| delete忘れ | ヒープ領域のメモリが解放されない原因になる |
newとdeleteを使うと、スタック領域ではなくヒープ領域にインスタンスを作れます。
スタック領域のインスタンスは、関数が終わると自動的に消えます。
ヒープ領域のインスタンスは、自分でdeleteするまで残ります。
そのため、newを使うと生成と消去のタイミングを自分で制御できます。
ただし、その分だけ、deleteで消去する責任も自分にあります。
ドラゴンボール風に言えば、newは修行カプセルをヒープ領域に召喚する技です。
deleteは使い終えた修行カプセルをきちんと片付ける技です。
この2つをセットで覚えると、C++のメモリ管理がかなり理解しやすくなります。
