C言語編で動的なメモリ確保について説明しました。そのとき使ったのが、malloc関数とfree関数でした (第55章参照)。C++でも、もちろんこれらの関数を使用して動的なメモ リ確保が行えますが、通常は使いません。代わりに使われるのが、この章で紹介する new演算子とdelete演算子です。
new演算子を使うとメモリの動的確保が行え、delete演算子によって解放することができます。ここで注意して ほしいのですが、newとdeleteは演算子です。mallocやfreeは関数です。この違いには重要な意味があるのですが、 現段階では知る必要はありません(第17章で演算子のオーバーロードについて説明しますが、 これと関係します)。
まずは単純にint型1つ分の領域を動的確保してみます。
#include <iostream>
int main()
{
int *p;
p = new int(); // int型の領域を動的確保
*p = 123;
std::cout << *p << std::endl;
delete p; // 動的に確保した領域を解放
return 0;
}
|
new演算子は、その直後に書いた型の領域を確保します。上の例ならint型になります。intの直後に付いている 括弧には、コンストラクタに渡す引数を指定できます。new演算子は、メモリの確保と共に、コンストラクタを呼び出しま す。実はC++では、int型のような基本データ型にもコンストラクタがあります。試しに、( )の中に123と書き、 直後の *p = 123; をコメントアウトして試してみると、確かに123と出力されます。よってC++では、次のどの方法を使っ ても、初期化処理が行えます。
int num = 100; int num(100); p = new int(100);
もちろん3つ目の方法では、pがポインタですし、使い終わったらにdeleteを呼ぶ必要があります。ついでに話して おくと、C++では「代入」と「初期化」は異なります。コンストラクタが呼び出されるのは「初期化」のときです。
delete演算子の使い方は、newで確保した領域を指すポインタを、deleteの直後に記述するだけです。newで確保 した領域でないと解放は行えません(malloc関数で確保した領域をdeleteで解放してはいけない)。なお、NULL で初期化されているポインタをdelete演算子に指定すると、何も起こらないことが保証されています。
new演算子がコンストラクタを呼び出しているのと同じで、delete演算子はデストラクタを呼び出します。
次にnew演算子を使って、配列用の領域を確保してみます。
#include <iostream>
int main()
{
int *p;
int i;
p = new int[10]; // int型10個分の領域を動的確保
for(i=0; i<10; ++i)
{
p[i] = i;
std::cout << p[i] << std::endl;
}
delete [] p; // 動的に確保した領域を解放
return 0;
}
|
new演算子を使って配列を確保するには、型指定の直後に[]を使って、配列に含まれる要素数を指定します。 上の例を見て分かるように、配列を動的確保する場合に()はありません。つまり、コンストラクタに引数を渡せませ ん。配列を動的確保する場合、コンストラクタは呼び出されないのではなく、引数がないデフォルトのコンスト ラクタが呼び出されています。このように、呼び出すコンストラクタを指定しなかった(できなかった)場合に、 呼び出される、引数のないコンストラクタをデフォルトコンストラクタといいます。 あるクラスに関して、1つも明示的にコンストラクタを宣言していない場合、コンパイラが自動的にデフォルトコン ストラクタ生成します。1つでもコンストラクタを宣言している場合には、このような自動的な生成は行われませ ん。
重要なのはdelete演算子の使い方です。new演算子を使って配列を確保した場合、delete演算子にも[]を付けなけ ればなりません。これを付けないと、正しく解放できません(恐らく、配列の先頭の要素についてだけ解放されるが、 それすらも保証されている訳ではない)。この[]の付け忘れは非常によくある間違いです。コンパイルは普通に成功 してしまうので気をつけて下さい。
もちろん、new演算子を使ってクラスをインスタンス化できます。
#include <iostream>
int main()
{
CSample* p;
p = new CSample( 100 );
std::cout << p->Get() << std::endl;
delete p;
return 0;
}
|
使い方は全く同じです。当然、コンストラクタを呼び出せます(クラスを配列としてインスタンス化する場合は除く)。 new演算子を使ってインスタンス化する場合、そのメンバ変数やメンバ関数をアクセスするときに、アロー演算子を使うこと になります。まあポインタ経由のアクセスになるので、当然といえば当然です。
delete演算子についても特殊なことはありません。ここでも[ ]を付けるか付けないかには注意が必要です。
C++では通常、malloc関数(callocやreallocも含む)を使いません。その理由は簡単で、malloc関数ではコンスト ラクタが呼び出せないからです。デフォルトコンストラクタも呼び出されません。同様に、free関数では デストラクタが呼び出されません。