C++のnew deleteの使い方についてわかりやすく解説

C++において、メモリの動的確保と解放は非常に重要な機能です。

その中でもnew deleteは、C++におけるメモリの動的確保に必須のスキルです。

そこで本記事では、C++のnew deleteの基本的な使い方から応用的な使い方まで詳しく解説します。

目次

new deleteとは

new deleteは、C++において動的メモリの確保と解放を行うための演算子です。

静的なメモリ確保である配列や変数とは異なり、プログラム実行中に必要な分だけメモリを確保することができます。また、不要になったメモリを解放することもできます。

new演算子は、指定された型のオブジェクトを動的に生成し、そのオブジェクトへのポインタを返します。delete演算子は、new演算子で確保したメモリ領域を解放します。

例えば、以下のようにしてint型の変数を動的に生成することができます。

int* p = new int;

この場合、int型のサイズ分だけメモリが確保され、そのアドレスがpに格納されます。この領域はdelete演算子で解放することができます。

delete p;

new deleteはC言語のmalloc freeよりも安全かつ使いやすく、C++ではmalloc freeの代わりに使用されています。

しかし、誤った使い方をするとプログラム実行時にエラーやメモリリークが発生する可能性があるため注意が必要です。

new deleteの基本的な使い方

C++において、new演算子とdelete演算子は動的なメモリ割り当てを行うために使用されます。

new演算子は、指定された型のオブジェクトを動的に割り当てることができます。

一方、delete演算子は、new演算子で割り当てられたメモリを解放することができます。

単一のオブジェクトの動的な割り当て

単一のオブジェクトを動的に割り当てる場合、以下のように記述します。

int* p = new int;

この例では、int型の変数を指すポインタpが宣言され、new演算子によってint型のサイズ分だけメモリが割り当てられます。この時点ではまだ初期化されていないため、pが指すアドレスに対して値を代入する必要があります。

*p = 10;

これでpが指すアドレスに10が代入されました。この後は通常の変数と同様に扱えます。

配列の動的な割り当て

配列を動的に割り当てる場合も同様です。以下はint型の配列を5つ分動的に割り当てる例です。

int* p = new int[5];

この場合も先ほどと同様に初期化する必要があります。

for (int i = 0; i < 5; i++) {
    p[i] = i;
}

これでp[0]からp[4]まで順番に0から4まで代入されました。

オブジェクトの動的な割り当てと初期化

オブジェクトを動的に割り当てる場合も同様です。以下はstringクラスのオブジェクトを1つ分動的に割り当てる例です。

//初期化時に引数を持つコンストラクタを呼べる
string* p = new string("hello");

初期化した後は通常のstringクラスと同じように扱えます。

cout << *p << endl; // "hello" と表示される

オブジェクトの動的な割り当てとデストラクタの呼び出し

new演算子でオブジェクトを生成した場合、そのオブジェクトは明示的なdelete演算子呼び出しまたはプログラム終了時まで存続します。

delete演算子でオブジェクトを破棄することによって、予め定義されたデストラクタを呼び出して、破棄する直前に行う処理を実行することが可能です。

以下はMyClassクラスのオブジェクトを1つ生成し、明示的なdelete演算子呼び出し後でもデストラクタが正しく呼ばれるか確認する例です。

class MyClass {
public:
    MyClass() { cout << "MyClass constructor called" << endl; }
    ~MyClass() { cout << "MyClass destructor called" << endl; }
};

int main() {
    MyClass* p = new MyClass();
    delete p;
}

実行結果:

MyClass constructor called
MyClass destructor called

以上がC++でnew delete演算子を使った簡単なメモリ管理方法です。

new deleteの注意点

new deleteを使用する際には、以下のような注意点があります。

メモリリークの防止

newで割り当てたメモリは、必ずdeleteで解放する必要があります

解放しない場合、メモリリークが発生し、プログラムの動作が不安定になる可能性があります。

特に長時間実行されるプログラムでは、メモリリークが蓄積されていくため、注意が必要です。

以下は、メモリリークを引き起こす例です。

int* p = new int;

// 2回目のnewで1回目に割り当てられたアドレスを失うが、
//メモリは確保されたまま(メモリリーク)
p = new int; 

この場合、2回目のnewで1回目に割り当てられたアドレスを失ってしまいます。その結果、1回目に割り当てられたメモリは解放されずに残ってしまい、メモリリークが発生します。

オブジェクトの動的な割り当てと例外の扱い方

オブジェクトを動的に割り当てる場合、コンストラクタで例外が発生した場合にどうするか考慮する必要があります。例えば以下のようなコードです。

class MyClass {
public:
    MyClass() {
        // コンストラクタ内で何らかの処理中に例外発生
        throw std::runtime_error("error");
    }
};

int main() {
    try {
        MyClass* p = new MyClass();
    } catch (std::exception& e) {
        std::cout << e.what() << std::endl;
    }
    delete p;
}

この場合、MyClassオブジェクトを動的に割り当てる際にコンストラクタ内で例外が発生しています。そのため、try-catch文で例外をキャッチしてプログラムを継続し、動的確保されたメモリを解放できるようにしています。

通常の変数はスコープを抜けると自動的に破棄されますが、動的確保されたメモリは開放されないので、予期しない例外が発生した場合でも正しくdelete演算子を呼べるようにしておくことが重要です。

オブジェクトの動的な割り当てとスマートポインタ

C++11以降では、「スマートポインタ」と呼ばれる仕組みが導入されました。スマートポインタは自動的にdeleteを呼び出すため、メモリリークを防止することが出来ます。

以下はunique_ptrというスマートポインタを使用した例です。

#include <memory>

class MyClass {};

int main() {
    std::unique_ptr<MyClass> p(new MyClass());
}

unique_ptrMyClassオブジェクトを指すポインタとして振る舞います。

unique_ptrオブジェクト自体もポインタなので、通常のポインタ同様にアロー演算子や参照演算子を使ってMyClassオブジェクトにアクセスすることが出来ます。

またunique_ptrオブジェクト自体もスコープから抜ける際(つまりデストラクタ呼び出し時)に自動的にdeleteされます。

#include <memory>

class MyClass {};

int main() {
    
    {
        std::unique_ptr<MyClass> p(new MyClass());
        // 何らかの処理...
    }
    //スコープを抜けると
    //unique_ptr内で自動的にdelete演算子が呼ばれて解放される
}

これによって明示的なdelete呼び出しやRAII(Resource Acquisition Is Initialization)パターンなど不要となり、意図しないメモリリークを起こしにくくなります。

終わりに

以上が、C++のnew deleteについての基本的な使い方や注意点でした。

動的なメモリ割り当ては、プログラムの効率性や安全性を高める上で重要な機能ですが、誤った使い方をするとメモリリークや例外の発生など、深刻な問題を引き起こすこともあります。

正しい使い方を理解し、適切に利用するように心がけましょう。

目次