#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のトップページをご覧ください。