オブジェクトとnewとdelete
オブジェクト
オブジェクトとはクラスをデータ型とした変数を宣言したり、
この後説明するnewと呼ばれる動的確保関数を使用して
メモリ上にクラスの領域を確保できた実体のあるデータのことです。
インスタンスと呼ばれることもあります。
また、オブジェクトを作成することを「オブジェクト化」「インスタンス化」と呼んでいます。
寿命が決まっているオブジェクト
クラスをローカル、またはグローバル領域で変数宣言することで、
寿命が決まっているオブジェクトを作成することができます。
ローカルオブジェクト
ローカルオブジェクトとはローカル領域(関数の中)で変数として宣言されたクラスのことです。
ローカルオブジェクトの変数の寿命はローカル変数と同じなので、
宣言を行った行で作成され、宣言されたスコープから外れたら破棄されます。
class Employee
{
public:
int Number;
};
void main(void)
{
// ローカルオブジェクト作成
Employee suzuki;
suzuki.Number = 1001;
}
suzukiというオブジェクトはmain関数内で作成されたのでローカルオブジェクトです。
この変数の寿命はmain関数のスコープが外れる(関数終了)までとなります。
グローバルオブジェクト
グローバルオブジェクトとはグローバル領域で変数宣言されたオブジェクトのことで、
グローバル領域は関数の外側を指します。
このグローバルオブジェクトはプログラム起動時に作成され、
プログラム終了時に破棄されまでアクセスが可能なので、
実質的にプログラム実行中は破棄されることはありません。
class Employee
{
public:
int Number;
};
// グローバルオブジェクト作成
Employee g_Sato;
void main(void)
{
g_Sato.Number = 1002;
}
g_Satoはグローバル領域で宣言されているのでグローバルオブジェクトです。
このオブジェクトの寿命はプログラム実行時から終了までなので、
プログラム中に破棄されることはありません。
寿命が決まっていないオブジェクト
newを使用して動的にオブジェクトを作成した場合、
寿命が決まっていないオブジェクトを作成できます。
動的オブジェクト
動的オブジェクトとはnewとdeleteを使用して
オブジェクトの作成と破棄の管理をプログラマー側で行うオブジェクトの事です。
このオブジェクトの寿命の管理はプログラマーが行うので必要な時に作成して、
不要になった時に破棄できるというメリットがありますが、破棄が自動で行われないので
破棄を忘れるとメモリが解放されないというデメリットもあります。
C言語にもmallocとfreeという動的に作成する方法がありますが、
この方法を使用して作成と破棄を行っても、コンストラクタとデストラクタが
実行されないのでC++では必ずnewとdeleteを使用します。
class Test
{
public:
Test(void);
~Test(void);
};
Test::Test(void)
{
printf("コンストラクタ\n");
}
Test::~Test(void)
{
printf("デストラクタ\n");
}
void main(void)
{
Test* p1 = NULL;
Test* p2 = NULL;
p1 = new CTest();
p2 = (Test*)malloc(sizeof(Test));
delete(p1);
free(p2);
}
実行結果:
コンストラクタ
デストラクタ
上の例でnewとdelete、mallocとfreeを使用して
Testクラスの作成と破棄を行っていますが、
結果の通りコンストラクタとデストラクタが一度しか実行されておらず、
この二つの関数の呼び出しはnewとdeleteが行っています。
クラスにとってコンストラクタ、デストラクタは魅力的な機能ですので、
何かしらの理由がない限り、動的オブジェクトでは
new、deleteを使用した方がいいと思います。
解放忘れに注意
newとdeleteを行う場合そのオブジェクトの作成と破棄の管理は
プログラム側で請け負います。
すなわち、確保を行ったオブジェクトの破棄を行う責任を負うということです。
オブジェクトの破棄を忘れてしまったらメモリリークが発生するので、
newを行ったらdeleteを行うということを忘れないようにしてください。
アロー演算子
動的オブジェクトはポインタ型になります。
クラスのポインタ型のメンバにアクセスするには構造体と同じように
「.」ではなく「->」(アロー演算子)を使用します。
// 書式例
クラス変数名->メンバ
// 具体例
class Test
{
public:
int Val;
};
void main()
{
Test *test = new Test;
test->Val = 1000;
printf("%d\n" test->m_Val);
delete test;
}
実行結果:
1000
new
newはmallocと同じでメモリを動的に確保してくれます。
mallocと異なるのはmallocはサイズ指定でメモリを確保しますが、
newは型指定でメモリの確保を行います。
// 書式
new データ型() // 引数が無ければ「()」は無くても可
// 具体例
// クラスデータCTest型のメモリを確保
Test *test = new Test
コンストラクタの指定
new演算子を使用した場合にデータ型に()を付けなかった場合は
自動的にデフォルトコンストラクタが選択されます。
もし、デフォルトコンストラクタ以外を指定したい場合は
「new データ型」の後ろに「()」を付けて、その中に引数を追加し、
使用するコンストラクタを決めます。
※デフォルトコンストラクタ以外を使用する場合はクラスで
使用するコンストラクタの定義を行っている必要があります。
class Test
{
public:
Test();
Test(int val);
~Test();
};
Test::Test()
{
printf("コンストラクタその1\n");
}
Test::Test(int val)
{
printf("コンストラクタその2 val = %d\n", val);
}
Test::~Test()
{
printf("デストラクタ");
}
void main()
{
Test* p1;
Test* p2;
// デフォルトコンストラクタ
p1 = new Test;
// int型の引数を必要とするコンストラクタ
p2 = new Test(100);
delete(p1);
delete(p2);
}
実行結果:
コンストラクタその1
コンストラクタその2 val = 100
上記のように複数のコンストラクタを用意しておけば
オブジェクト化する際に引数の指定の仕方で
どのコンストラクタを使用するかを選ぶことができます。
delete
deleteはfreeと同じで動的に確保したメモリを解放します。
// 書式
delete 解放オブジェクト;
具体例
Test* test = new Test;
delete test;
デストラクタはオーバーロードをすることができないので、
書式は変わりませんが、例外として配列の破棄があります。
// 配列解放の書式
delete[] 解放オブジェクト
// 具体例
Test* list = new Test[5];
delete[] list;
上のコードのように配列を破棄する場合は
deleteの後ろに「 [] 」をつけます。