C++動的メモリ管理完全解説:スタックとヒープ、new/delete
C++で動的メモリが実際にどのように機能するかをよく理解することは、優れたC++プログラマーになるために不可欠です。C++プログラムのメモリは2つの部分に分かれています:
スタック : 関数内で宣言されたすべての変数はスタックからメモリを取得します。
ヒープ : プログラムの未使用メモリであり、プログラム実行時に動的にメモリを割り当てるために使用できます。
多くの場合、定義済み変数に特定の情報を格納するためにどれだけのメモリが必要かを事前に知ることはできず、必要なメモリサイズは実行時に決定されることがあります。
実行時にヒープ内で、与えられた型の変数のためにメモリを割り当てるには、割り当てられたスペースのアドレスを返すC++の特殊な演算子を使用します。この演算子を new 演算子と呼びます。
動的に割り当てられたメモリが不要になった場合、delete 演算子を使用して、new 演算子によって以前に割り当てられたメモリを解放できます。
new と delete 演算子
new 演算子を使用して任意のデータ型のメモリを動的に割り当てる一般的な構文:
new data-type;
ここで、data-type は配列を含む組み込みデータ型、またはクラスや構造体を含むユーザー定義データ型のいずれでもかまいません。組み込みデータ型から始めましょう。例えば、double 型へのポインタを定義し、実行時にその変数のためにメモリを割り当てるよう要求できます。new 演算子を使用して以下のように行います:
double* pvalue = NULL; // ポインタをNULLで初期化 pvalue = new double; // 変数のためにメモリを要求
フリーストアが使い果たされている場合、メモリが正常に割り当てられない可能性があります。したがって、new 演算子がNULLポインタを返すかどうかをチェックし、適切な対応を取るのが良い習慣です:
double* pvalue = NULL;
if( !(pvalue = new double )) {
cout << "Error: out of memory." << endl;
exit(1);
}
C言語の malloc() 関数はC++にも存在しますが、malloc() 関数の使用は避けることを推奨します。new が malloc() より優れている主な利点は、new が単にメモリを割り当てるだけでなく、オブジェクトを構築することであり、これがC++の主目的だからです。
動的に割り当てられた変数が不要になったと判断した時点で、その変数がフリーストアで占有しているメモリを『delete』演算子で解放できます:
delete pvalue; // pvalueが指すメモリを解放
上記概念を組み合わせて、『new』と『delete』の動作を示す例:
Live Demo#include <iostream>
using namespace std;
int main () {
double* pvalue = NULL; // ポインタをNULLで初期化
pvalue = new double; // 変数のためにメモリを要求
*pvalue = 29494.99; // 割り当てられたアドレスに値を格納
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue; // メモリを解放
return 0;
}
上記コードをコンパイルして実行すると、以下の結果が得られます:
Value of pvalue : 29495
配列の動的メモリ割り当て
例えば、文字の配列、つまり20文字の文字列のためにメモリを割り当てたいとします。上記で使用した同じ構文を使って、以下のように動的にメモリを割り当てできます:
char* pvalue = NULL; // ポインタをNULLで初期化 pvalue = new char[20]; // 変数のためにメモリを要求
作成した配列を削除する文は以下のようになります:
delete [] pvalue; // pvalueが指す配列を削除
同様に、new 演算子の一般的な構文に従って、多次元配列のメモリを割り当てできます:
double** pvalue = NULL; // ポインタをNULLで初期化 pvalue = new double [3][4]; // 3x4配列のメモリを割り当て
しかし、多次元配列のメモリ解放構文は上記と同じままです:
delete [] pvalue; // pvalueが指す配列を削除
オブジェクトの動的メモリ割り当て
オブジェクトは単純なデータ型と何ら変わりません。例えば、以下のコードではオブジェクトの配列を使用して概念を明確にします:
Live Demo#include <iostream>
using namespace std;
class Box {
public:
Box() {
cout << "Constructor called!" << endl;
}
~Box() {
cout << "Destructor called!" << endl;
}
};
int main() {
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // 配列を削除
return 0;
}
4つのBoxオブジェクトの配列を割り当てると、単純なコンストラクタが4回呼び出され、同様にこれらのオブジェクトを削除するとき、デストラクタも同じ回数呼び出されます。
上記コードをコンパイルして実行すると、以下の結果が得られます:
Constructor called! Constructor called! Constructor called! Constructor called! Destructor called! Destructor called! Destructor called! Destructor called!
C言語
- C++データ型完全ガイド:基本型、修飾子、ベストプラクティス
- C++演算子完全ガイド:算術・代入・関係・論理・ビット演算子と実例
- C++コメント完全ガイド:読みやすく保守しやすいコードのためのベストプラクティス
- C言語動的メモリ管理:calloc、malloc、freeの解説
- C++プログラミング言語:概要と主要機能
- C++定数とリテラル完全解説:型、構文、ベストプラクティス
- C++演算子完全ガイド:種類、使い方、ベストプラクティス
- C++数値型完全ガイド:実践的な使い方と数学関数
- C++リファレンス完全理解:重要概念とポインタとの違い
- C++動的メモリ管理完全解説:スタックとヒープ、new/delete
- C++テンプレート完全ガイド:ジェネリックプログラミングの基礎