C++ new delete 書いて覚えるための初心者自己中記事
プログラム実行中に動的に変数を作る。
そのために使うのが new と delete 。
動的に変数を作るとはどういうことかというと
-----------------------------------------------
int main(){
int size;
cin >> size;
int* p = new int[size];
}
-----------------------------------------------
こんな感じ。
要素数を入力したら、宣言した型の必要バイト数を基準に入力した要素数分のアドレスを確保してポインタ変数に渡す。
必要なのは new の部分。
これは new の右に書いた型が必要とする範囲のアドレスを確保してそのアドレスを返す者らしい。 new すげぇ。
型だけでもいいし、何かの型の配列でもOK
そもそも、 new つけないで宣言時に同じことをしたらダメなの?
と思ったけどダメなようで、
-----------------------------------------------
int main(){
int size;
cin >> size;
int a[size];
for(int i = 0 ; i < size ; ++i){
a[i] = i;
};
for(int i = 0 ; i < size ; ++i){
cout << a[i] << " : " << flush;
};
}
(結果)
5
0 : 1 : 2 : 3 : 4 :
-----------------------------------------------
ってアレ?
出来たよ・・
テキストには、配列変数を宣言する時の[ ] に変数は入れられないって書いてるけど。
cin は特別なのか?
-----------------------------------------------
int main(){
int size = 10;
// cin >> size;
int a[size];
for(int i = 0 ; i < size ; ++i){
a[i] = i;
};
for(int i = 0 ; i < size ; ++i){
cout << a[i] << " : " << flush;
};
}
(結果)
0 : 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 :
-----------------------------------------------
いや違うなぁ・・
普通の変数入れられるよ。
-----------------------------------------------
int main(){
int size = 10;
int a[size];
printf("%d\n",(int)(sizeof a / sizeof a[0]));
}
(結果)
10
-----------------------------------------------
変数で要素数指定できるなぁ。
俺が何かを勘違いしているはず。なんだ・・・?
google先生に聞いたけど、いまいちはっきりわからん。
gccってコンパイラなら対応していて、俺の環境はgccを使っているからできる?
という感じかもしれない。
うん、そういうことで。
で、次に new を使ったら必ずセットでやらなければいけないこと。 delete だ。
-----------------------------------------------
int main(){
int size;
cin >> size;
int* p = new int[size];
delete p;
}
-----------------------------------------------
new で確保したアドレス範囲は消えない。
通常のローカル変数は関数を抜けた時にデストラクタで消されるけど、
new で確保したアドレスは生き残る。
生き残るのでいつまでもメモリに残ってしまう。
このポインタp を別のアドレスで書き換えてしまった場合 new が確保してくれたアドレスがどこなのかわからなくなってしまう。
自動でメモリ解放しないし、アドレスもわからないから消せない。となってしまう。
なので必ず自分の手で delete する。
使い方は delete アドレス
配列の場合は delete[ ] アドレス
削除するタイミングが自分で決められるのがいいところなのだそうだ。
それにしても関数抜けても消されないって静的変数みたいですね。
もしかして初期化も一回しか行われないとか?
確認してみよう。
-----------------------------------------------
void Test(int b){
int size;
cin >> size;
int* p = new int[size];
printf("%p\n",p);
for(int i = 0 ; i < size ; ++i){
p[i] = i;
};
for(int i = 0 ; i < size ; ++i){
cout << p[i] << " : " << flush;
};
cout << endl;
if(b == 1){
delete p;
cout << "deleteされました" << endl;
}
}
int main(){
Test(0);
Test(0);
Test(1);
}
(結果)
7
0x1003020e0
0 : 1 : 2 : 3 : 4 : 5 : 6 :
2
0x100500090
0 : 1 :
5
0x100500130
0 : 1 : 2 : 3 : 4 :
deleteされました
-----------------------------------------------
というかよく考えたらすごいバカなこと言ってたのに気づいた。
new int [size] で毎回新しいアドレス確保してるんだから初期化も何もないですよね。
そんでもってこのやり方がやってはいけない事そのものでした!
delete したのは3回目のアドレスだけ。1回目と2回目は放置してポインタp に別のアドレスを入れてしまっている・・・。
いやアドレス入れる以前に関数抜けたらポインタp は消えるんじゃ・・
該当アドレスの解放はしないけどポインタp っていう変数は消されてしまうのでは?
なるほどそういうことか、関数抜ける前には必ず delete しなければいけないと覚えよう。
あと、ポインタp にアドレスいれてるだけなのでスコープも普通のローカル変数と同じですね。
delete しないと消されない。
初期化は毎回行われる。
new は必ずアドレスを確保してくれるわけではないらしい。
なんだよそれ・・
なので new が失敗した時の対策がある。
それが nothrow だ。
使い方は new(nothrow) と new の後ろにつけるだけ。
こうするとアドレス確保に失敗した場合にアドレスではなくヌルポインタを返してくれます。
-----------------------------------------------
int main(){
int size;
cin >> size;
int* p = new(nothrow) int[size];
if(p != NULL){
for(int i = 0 ; i < size ; ++i){
p[i] = i;
};
for(int i = 0 ; i < size ; ++i){
cout << p[i] << " : " << flush;
};
cout << endl;
delete[] p;
}else{
cout << "失敗しました" << endl;
}
}
(結果)
-3
失敗しました
-----------------------------------------------
こうですね。うまくいきました。
ちなみに nothrow を使うには #include <new> が必要です。
とりあえずここまで