#pragma twice

KAB-studio > プログラミング > #pragma twice > 213 Version 11.13 new と delete とそのほか色々

#pragma twice 213 Version 11.13 new と delete とそのほか色々

前のページへ 表紙・目次へ 次のページへ

 Version 11.13
new と delete とそのほか色々

今回は、 new と delete の残りの部分について見ておきます
まず前回の続きからよね
そう、 new と delete について。まず、正確に言うと、 new と delete 
にはメモリを確保する機能はありません
ええっ!? だって、確保してるじゃない
それは、他の関数を呼び出してるから。 new には

・メモリを確保する関数を呼び出す。
・コンストラクタを呼び出す。

っていうふたつの機能があるんです。同じくデストラクタには

・デストラクタを呼び出す。
・メモリを解放する関数を呼び出す。

っていう機能があります
コンストラクタとデストラクタで順序逆~
だって、解放したあとじゃ変数ないんだから、デストラクタは呼べないで
しょ?
あ……そっか、そういえば
こういうのも重要だから気をつけてね。で、この new が呼ぶ〈メモリを
確保する関数〉とか delete が呼ぶ〈メモリを解放する関数〉って、実は自
由に変えられるんです
好きなのでできるってこと? たとえば malloc() とか?
そういうこと。で、この関数は、 MFC のプログラムなら……あ、見た方
が早いかな

void NewAndDelete()
{
    // int を作成。
    int *pi = new int;
    // そして解放。
    delete pi;
}

で、 new してるところにブレークポイントを置いて
ビルドして実行、んでステップイン!

void* AFX_CDECL operator new( /* 略 */ )
{
    return ::operator new( /* 略 */ );
}

って関数に入った。 operator って確か、クラスに演算子が使えるように
するのだよね
そうそう、 Version 5.22 ( No.087 ) や Version 7.09 ( No.129 ) で
やったね。つまり new は演算子のひとつで、こういうふうに書くことで 
new した時にこの関数を呼ぶようにさせることができるってこと
あれ、でも new したのって int だったよ。クラスじゃないじゃん
そう、これはクラス以外のものに new をした場合のもの。こういう 
operator のは、別にクラスだけに使えるわけじゃないから
へー
 operator については今度ちゃんと説明するとして、この中でもう一段階 
new を呼んでるでしょ
んじゃそっちに入るね。ほい

void* __cdecl operator new( /* 略 */ )
{
// 略
    return ::operator new(nSize);
// 略
        pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);
// 略
}

む、 new と、あと malloc() もどきがある
リリースビルドの時は上の方が、デバッグビルドの時には下の方が呼ばれ
ます
リリースビルド……ってなんだっけ
 Version 3.24 ( No.049 ) と Version 6.18 ( No.118 ) を参照
あ、プログラム切り替えるやつね。デバッグするときはデバッグモードに
しないと ASSERT() が使えないとか
そうそう。で、リリースビルドの時には普通の new が呼ばれます。これ
はあとで。デバッグモードの時には _malloc_dbg() が呼ばれます。これは
malloc() のデバッグ版
デバッグ版?
本当は正しい表現じゃないんだけどね。 Version 11.09 ( No.209 ) でメ
モリリークって教えたでしょ
メモリを確保しておいて解放しないのだよね
そうそう。あのとき、アプリを終了したときにメモリリークがあったって
表示されてたけど、その機能があるのは _malloc_dbg() の方なんです
え? だって、あのときだって普通に malloc() 使ってたよ?
実は、 malloc() はデバッグモードだと _malloc_dbg() を呼び出すんで

あ、そういうこと
この _malloc_dbg() については次回に説明するから置いておいて、結局
は new も malloc() 使ってるってこと
でもリリースビルドの時は new を使ってるんでしょ?
そっちも見ておいた方がいいか。実は、この new の機能は MFC を使っ
てるから、素の new を見るには MFC なしじゃないといけないんです
でもここで使ってるんでしょ?
でもそれはリリースビルドの時専用だから
あ、デバッグモードじゃないからステップインとかできない……
そういうこと。そこで、 Version 8.01 ( No.143 ) の、 SDK だけのを
引っ張ってきて

// 最初に呼ばれる関数です。
int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // int を作成。
    int *pi = new int;
    // そして解放。
    delete pi;
// 略
}

っていうふうにして、 new してるところでブレークポイント置いて
なんかかなり回りくどいね……ほい、んでステップイン!

void * operator new( unsigned int cb )
{
        return _nh_malloc( cb, 1 );
}

ってのに入ったよ。あ、また malloc() もどき
これは素の new なんだけど、この素の new も
 malloc() 系を使っている、と
そういうこと
あ、でもさ、 malloc() 系も API のを使ってるんでしょ? だったら
そう、最終的には、メモリ確保はすべて API で行われてるってこと
それって前にも何度かあったよね。ランタイムや MFC とか使ってても、
結局本当のところは API を呼び出してるんだって
そういうこと。それが重要だから
……ん? じゃあなんで new と delete なんだっけ
コンストラクタとデストラクタ
そうだった。 new はコンストラクタを呼んでくれて、 delete はデスト
ラクタを呼んでくれるんだよね
その機能があるから、 new と delete は必ず使う必要があるってこと
なるほど
ただ、 new と delete には、実は大きな問題があるんです
げ、なにそれ
今まで new で int とかしか作ってこなかったけど、配列の時にはちょっ
と特殊になるんです

void CreateNewArray()
{
    // このデータが入る領域を確保します。
    const char *const DATA = "あいうえお";

    // まず配列を確保。
    char *pch = new char[strlen( DATA ) + 1];

    // コピーしてから出力。
    strcpy( pch, DATA );
    TRACE( "%s\n", pch );

    // 解放します。
    delete[] pch;
}

サイズは [] の中で指定するんだ。配列っぽいね
うん、キャストも必要ないし、 new の方はわかりやすいんだけどね
ってことは delete ……あ、 delete にも [] が付いてる
実は、 new[] で配列を確保したら、必ず delete[] で解放しなきゃいけ
ないんです
 int 一個とかならいいんだよね、今まで見てると
そう、配列の時だけ
間違えて [] 付けないで解放しちゃったらどうなるの?
そうすると、デストラクタが最初の要素だけしか呼ばれないんです。たと
えば

void CreateNewCStringArrayBad()
{
    // まず配列を確保。
    CString *pcStr
        = new CString[10];
    // 誤った方法で解放します。
    delete pcStr;
}

ってすると、残りの9個の CString は
デストラクタが呼ばれない……うわ、実行したらエラーになった
だから必須ってこと
ってことはさ

malloc() - free()
GlobalAlloc() - GlobalFree()
new - delete
new[] - delete[]

って、4ペアになるんじゃない?
そういうことだね。だから、 new と delete を使う時にも必ずクラスの
中に入れて、コンストラクタとデストラクタで確保と解放がされるようにす
るのがいいかな
って、そのコンストラクタとデストラクタを呼べるのが new と delete 
で、なんか鶏が先か玉子が先かっぽい……

/*
    Preview Next Story!
*/
なんか、難しくないけどややこしいってゆーか
ま、パターンなんだけどね
ペアとか?
そうそう。いろいろあっても、基本は同じだったり
細かいところは違っても、だいたい似たり寄ったりだもんね
というわけで次回
< Version 11.14 メモリ確保とデバッグモード >
につづく!
ま、とりあえずそういうのに慣れるのがまずは重要かも
なんかヤダ

 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。