C++入門|newとdeleteの基本型への利用

int型の戦闘力も、ヒープ領域に一時召喚できる。newで確保し、ポインタで操作し、deleteで片付けるのがC++の基本メモリ修行です。

ここまで、new演算子とdelete演算子を使って、クラスのインスタンスをヒープ領域に生成し、使い終わったら消去する流れを学びました。

たとえば、修行カプセルのようなオブジェクトをnewで生成し、ポインタ変数を通して操作し、最後にdeleteで消去する形です。

しかし、new演算子とdelete演算子は、クラスだけに使うものではありません。

C++では、intやdoubleのような基本データ型に対しても、new演算子とdelete演算子を使えます。

たとえば、int型の値をヒープ領域に確保し、その場所に戦闘力の数値を入れて、使い終わったらdeleteで消去できます。

ドラゴンボール風にたとえるなら、戦士そのものをヒープ領域に召喚するのではなく、戦闘力スカウターの数値だけをヒープ領域に一時保存するイメージです。

操作C++の意味ドラゴンボール風のイメージ
int* pPower = NULL;int型を指すポインタを用意する戦闘力メーターの位置を記録するレーダーを準備
pPower = new int();int型の領域をヒープに確保するヒープ領域に戦闘力メーターを出現させる
*pPower = 530000;確保した領域に値を入れるメーターに戦闘力530000を記録する
cout << *pPower;確保した領域の値を表示するスカウターで戦闘力を表示する
delete pPower;確保した領域を解放する使い終わったメーターを片付ける

今回の記事では、int型のメモリ領域をnewで確保し、ポインタを使って値を操作し、最後にdeleteで消去する流れを確認します。

図:int型の戦闘力メーターをヒープ領域に生成する

この図が示していること

この図では、int型のメモリ領域をnewでヒープ領域に確保し、ポインタ変数pPowerでその場所を指している様子を表しています。

pPowerは、int型の値そのものではなく、int型の領域がある場所を記憶するポインタです。

実際の数値は、ヒープ領域に確保されたint型のメモリに入ります。
その値を操作するときは、*pPowerのように書きます。

基本データ型にもnewとdeleteは使える

new演算子というと、クラスのインスタンスを作るときに使うイメージが強いかもしれません。

しかし、C++では、次のような基本データ型にもnewを使えます。

基本データ型
int整数を保存する
double小数を保存する
booltrueまたはfalseを保存する
char1文字を保存する

今回のサンプルでは、int型の領域をヒープ領域に確保します。

int* pPower = NULL;
pPower = new int();

intはクラスではありません。
それでもnew int()と書くことで、int型の値を保存できるメモリ領域をヒープ領域に確保できます。

そして、使い終わったらdeleteで解放します。

delete pPower;

つまり、newとdeleteは、クラスのインスタンスだけでなく、intのような基本型にも使えるということです。

今回作成するサンプルプログラム

今回のプログラムでは、ヒープ領域にint型の領域を確保し、そこへ戦闘力の値を保存します。

流れは次のようになります。

手順処理内容
1int* pPower = NULL;int型を指すポインタを用意する
2pPower = new int();ヒープ領域にint型の領域を確保する
3*pPower = 530000;確保した領域に戦闘力を代入する
4cout << *pPower;戦闘力を表示する
5delete pPower;確保した領域を解放する

このサンプルでは、クラスは使いません。
int型という基本データ型を、newとdeleteで扱うことに集中します。

サンプルプログラム

プロジェクト/ファイル名: Chap4_03/main.cpp

#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    // int型の領域を指すポインタ変数を用意する
    int* pPower = NULL;

    // ヒープ領域にint型のメモリを確保する
    pPower = new int();

    // 確保した領域に戦闘力を代入する
    *pPower = 530000;

    // 戦闘力を表示する
    cout << "戦闘力:" << *pPower << "ポイント" << endl;

    // 動的に確保したint型の領域を消去する
    delete pPower;

    // 消去が終わったことを表示する
    cout << "戦闘力メーターを消去しました。" << endl;

    // プログラムが正常に終わったことを示す
    return 0;
}

実行結果は次のようになります。

戦闘力:530000ポイント
戦闘力メーターを消去しました。

このプログラムでは、new int()によってint型のメモリ領域をヒープ領域に確保しています。

その領域に*pPowerを使って530000を代入し、coutで表示しています。

最後にdelete pPowerで、確保したメモリ領域を解放しています。

int型のポインタ変数を用意する

最初に、int型を指すポインタ変数を用意しています。

int* pPower = NULL;

このpPowerは、int型の値そのものを入れる変数ではありません。
int型の値が置かれている場所、つまりアドレスを記憶するための変数です。

書き方意味
int*int型の領域を指すポインタ型
pPowerポインタ変数名
NULLまだどこも指していない状態

最初の段階では、まだヒープ領域にint型の領域を確保していません。

そのため、pPowerにはNULLを入れて、何も指していない状態にしています。

ドラゴンボール風に言えば、pPowerはスカウターが追跡するメモリ位置です。
最初はまだ戦闘力メーターが出現していないので、どこも指していません。

new int()でint型の領域を確保する

次に、new演算子を使って、int型の領域をヒープ領域に確保しています。

pPower = new int();

この処理によって、ヒープ領域にint型の値を1つ保存できる場所が作られます。

そして、その場所のアドレスがpPowerに代入されます。

部分意味
new int()int型のメモリ領域をヒープ領域に確保する
pPower =確保した場所のアドレスをpPowerに代入する

ここで大切なのは、pPowerに入っているのは530000のような値ではなく、値が置かれる場所の情報だということです。

実際の値を扱うには、*pPowerと書きます。

*pPowerで確保した領域にアクセスする

次の行では、*pPowerを使って値を代入しています。

*pPower = 530000;

pPowerは、ヒープ領域にあるint型の領域を指しています。
*pPowerと書くと、そのpPowerが指している先のint型の値を表します。

つまり、次のようなイメージです。

書き方意味
pPowerint型の領域の場所を指す
*pPowerpPowerが指している先にあるint型の値

そのため、次のように書くと、

*pPower = 530000;

ヒープ領域に確保したint型の場所へ、530000という値が入ります。

ドラゴンボール風に言えば、pPowerは戦闘力メーターの場所を指すレーダーです。
*pPowerは、そのメーターに表示される実際の戦闘力です。

図:pPowerと*pPowerの違い

この図が示していること

この図では、pPowerと*pPowerの違いを表しています。

pPowerは、ヒープ領域に確保されたint型の場所を記憶しています。
一方、*pPowerは、その場所にある実際の値を表します。

そのため、*pPower = 530000; と書くと、pPowerが指している先のint型領域に530000が代入されます。

値を表示する

次の行では、*pPowerの値を表示しています。

cout << "戦闘力:" << *pPower << "ポイント" << endl;

*pPowerには530000が入っています。

そのため、実行すると次のように表示されます。

戦闘力:530000ポイント

ここでも、表示しているのはpPowerそのものではなく、*pPowerです。

pPowerはアドレスを持つポインタ変数です。
表示したいのは、そこに入っている戦闘力の値なので、*pPowerを使います。

deleteで確保した領域を解放する

最後に、delete演算子で、newによって確保したint型の領域を解放します。

delete pPower;

deleteのあとには、newで確保した領域を指すポインタ変数を書きます。

書き方意味
delete pPower;pPowerが指しているヒープ領域のint型メモリを解放する

new int()で確保したメモリは、使い終わったらdeleteで解放する必要があります。

pPower = new int();

// 使う処理

delete pPower;

このnewとdeleteはセットで考えます。

ドラゴンボール風に言えば、newで戦闘力メーターをヒープ領域に出現させたら、修行が終わったあとにdeleteで片付ける必要があります。

delete後のポインタには注意する

delete pPower;を実行すると、pPowerが指していたヒープ領域は解放されます。

ただし、pPowerというポインタ変数自体が消えるわけではありません。

pPowerには、解放済みの場所を指していた情報が残っている可能性があります。

そのため、より安全にするなら、delete後にNULLを代入しておく書き方もあります。

delete pPower;
pPower = NULL;

こうしておくと、pPowerがもう有効な領域を指していないことが分かりやすくなります。

今回のような短いプログラムでは、deleteの直後にreturn 0;で終了しているため大きな問題にはなりにくいですが、長いプログラムではdelete後のポインタの扱いに注意が必要です。

delete後にアクセスしてはいけない

deleteしたあとに、次のように*pPowerへアクセスしてはいけません。

delete pPower;
cout << *pPower << endl;

delete pPower;によって、pPowerが指していた領域は解放されています。

そのあとに*pPowerを使うと、もう使ってはいけない場所を読もうとすることになります。

これは危険な動きです。

状態*pPowerを使ってよいか
new int()のあと使える
delete pPower;のあと使ってはいけない

修行カプセル風に言えば、deleteで戦闘力メーターを片付けたあとに、もう存在しないメーターを読もうとしているようなものです。

new int()の初期値について

今回のサンプルでは、次のように書いています。

pPower = new int();

このように、intのあとに()を付けると、基本的には値が0で初期化されます。

ただし、このサンプルではすぐ次の行で530000を代入しています。

*pPower = 530000;

そのため、表示される値は530000です。

もし、new intだけを書いて初期化しない場合、値が不定になる可能性があります。

pPower = new int;

入門段階では、初期化を意識してnew int()のように書くと安心です。

書き方意味
new int()int型領域を確保し、0で初期化する
new intint型領域を確保するが、値は不定になることがある
*pPower = 530000;確保した領域へ明示的に値を入れる

値を使う前には、必ず意味のある値を代入する習慣を付けると安全です。

図:new、代入、表示、deleteの流れ

この図が示していること

この図では、int型のメモリ領域をnewで確保し、*pPowerで値を代入し、coutで表示し、最後にdeleteで解放する流れを表しています。

クラスではなくintのような基本データ型でも、newとdeleteを使ってヒープ領域のメモリを扱えます。

大切なのは、newで確保したメモリを、使い終わったらdeleteで解放することです。

基本型にnewとdeleteを使う流れ

今回の流れをもう一度整理すると、次のようになります。

手順コード内容
1int* pPower = NULL;int型を指すポインタ変数を用意する
2pPower = new int();int型の領域をヒープに確保する
3*pPower = 530000;確保した領域に値を代入する
4cout << *pPower;値を表示する
5delete pPower;確保した領域を解放する

この流れは、基本型でもクラスでも考え方は似ています。

対象newで行うことdeleteで行うこと
クラスインスタンスを生成するインスタンスを消去する
intなどの基本型値を保存する領域を確保する確保した領域を解放する

ただし、クラスの場合はnewでコンストラクタが呼ばれ、deleteでデストラクタが呼ばれます。
intのような基本型にはコンストラクタやデストラクタはありませんが、メモリの確保と解放という考え方は同じです。

newとdeleteの基本型利用で押さえたいポイント

newとdeleteを基本データ型に使うときは、次のポイントを押さえておきましょう。

ポイント内容
基本型にもnewは使えるintやdoubleの領域をヒープに確保できる
ポインタ変数が必要確保した領域のアドレスを記憶する
値を扱うときは*を使う*pPowerで実際のint値にアクセスする
使い終わったらdeleteするnewで確保した領域は解放が必要
delete後にアクセスしない解放済みの領域を使うのは危険
delete後にNULLを入れると安全pPower = NULL;で何も指していない状態にできる

new演算子は、ヒープ領域にメモリを確保するための演算子です。
delete演算子は、newで確保したメモリを解放するための演算子です。

intのような小さな値でも、newを使えばヒープ領域に置けます。

ただし、newで確保したメモリは、使い終わったらdeleteで解放する必要があります。

ドラゴンボール風に言えば、newはヒープ領域に戦闘力メーターを出現させる技です。
*pPowerは、そのメーターの中の数値を操作する方法です。
deleteは、使い終わったメーターをきちんと消す後片付けです。

この流れを押さえておくと、C++の動的メモリ管理がかなり分かりやすくなります。