#pragma twice

KAB-studio > プログラミング > #pragma twice > 377 Version 17.22 delete の方法

#pragma twice 377 Version 17.22 delete の方法

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

 Version 17.22
delete の方法

「前回はポリモーフィズムを new と delete で行う方法を説明しました」
『でも delete のタイミングとか方法の管理が難しー! あんな面倒なこと
しなきゃいけないの? ってゆーかなんで new しなきゃいけないんだっけ』
「一番の問題は、 Version 17.12 ( No.367 ) 等で説明したとおり、
ポリモーフィズムはポインタでないと使えないから」
『あーそっか、アップキャストしても型が変わらないようにするにはアドレス
渡すしかないんだもんね』
「そういうこと。つまり、ポリモーフィズムを使うためには」

・変数のアドレスを使用する

「ことが必要です」
『でも』

・関数から抜けるとローカル変数がなくなる
 →戻り値でローカル変数のアドレスを返せない
  → new で変数を作るしかない

『ってことになって、だから面倒なことになるわけね』
「そういうこと。でも、そうなるといくつか問題点が発生します」

・ポインタをいつ解放するか
 ・使用中に解放してはいけない(存在しない変数を使ってしまう)
 ・解放し忘れてはいけない(メモリリークになる)
 ・解放前にアドレスを上書きしてはいけない(同上)

・delete で解放していいのか
 ・確保方法がわからないと解放できない
  ( new で確保したのか、それとも普通の変数なのか
   アドレスからはわからない)

「こういった問題をある程度回避する方法があります」
『なにぃ! そんな方法があるんなら早く教えてよ』
「ただ、ちょっと上級者向けなのと、結構面倒だからその辺は憶えておい
て」
『う、つまり理解せず使うなってことね』
「そゆこと。まず比較的簡単な」

・delete で解放していいのか

「から解決します。これには方法がふたつあります」

・delete をオーバーロードする
・解放専用のメンバ関数を作る

「まずは delete のオーバーロードから」

・delete をオーバーロードする

「まず、 Version 11.13 ( No.213 ) を読み返してください。こんな箇所が
あったと思います」

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

『うぉ、 operator って、演算子のオーバーロードのやつじゃん!』
「そう、 Version 16.15 ( No.342 ) とかで説明したね。実は、 new と 
delete は演算子のひとつ、ということになってるんです」
『へー、全然演算子に見えないけど』
「ちなみに sizeof も演算子。このみっつは記号じゃない演算子ってこと」
『なんか不思議……』
「で、この new や delete も演算子だから、オーバーロードできるんです。
というわけで、 delete 演算子をメンバ関数でオーバーロードしてみます」

// Main.cpp
#include <Windows.h>
#include <stdio.h>

// delete 演算子で解放できないクラス。
class CUnDeletable
{
public:
    // delete されたときに呼び出されるメンバ関数です。
    // delete 演算子をオーバーロードしています。
    void operator delete( void *p_pvThis )
    {
        OutputDebugString( "delete されました。\n" );
    }
};

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // CUnDeletable クラスの変数を作ります。
    CUnDeletable cUnDeletable;
    // この変数のアドレスをポインタに格納します。
    CUnDeletable *pcUnDeletable = &cUnDeletable;
    // それを delete で解放します。
    delete pcUnDeletable;
    // delete されました。

    return 0;
}

『ぎゃーっ!! 何これ気持ち悪ッ!!』
「普通の変数を delete で解放してるように見えるからね……まず、 
delete 演算子をメンバ関数でオーバーロードする場合、こういう引数と
戻り値になります」

    void operator delete( void *p_pvThis )
                                  ↑delete に渡されたアドレス。

「 p_pvThis 引数には delete に渡したアドレス、つまり」

    delete pcUnDeletable; ←このアドレスと同じ。

「この pcUnDeletable に入っているアドレスが渡されます」
『このアドレスでなにするの?』
「普通は、ここでメモリの解放を行います」
『そっか、それが delete だもんね』
「本来、 new と delete のオーバーロードは、デフォルトの方法じゃない
特殊な方法でメモリを確保したい場合に使います」
『 Version 11.13 ( No.213 ) のデバッグモードの時みたいにね』
「そゆこと。だから、本来なら new もオーバーロードして対にして確保と
解放を行うわけです。でも、今回は delete で何もしない、っていう形に
しました」
『だから、普通の変数を delete しても大丈夫なわけね』
「逆に言うと」

    // CUnDeletable クラスの変数を new で作成します。
    CUnDeletable *pcUnDeletable = new CUnDeletable;
    // それを delete で解放します。
    delete pcUnDeletable;
    // delete されました。
    ↑実際には解放されてないのでメモリリークします。

「とかするとメモリリークします」
『……えっと、これって何に使うんだっけ?』
「前々回の static 変数を使った場合の話」
『あー! そか、 static 変数を使った時は delete しちゃいけない、それ
を防ぐのがこれってわけね』
「そういうこと。これなら static 変数や普通の変数をうっかり delete し
ても大丈夫ってことだね」
『それはそうだけど……』
「あともうひとつ、似た方法に」

・解放専用のメンバ関数を作る

「というものがあります。使用例はこんな感じ」

// Main.cpp
#include <Windows.h>
#include <stdio.h>

// Delete() メンバ関数で解放するクラス。
class CDeletable
{
public:
    // 自分を解放するメンバ関数です。
    virtual void Delete()
    {
        OutputDebugString( "delete します。\n" );
        // 自分自身を delete します。
        delete this;
    }
};

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // CDeletable クラスの変数を new で作成します。
    CDeletable *pcDeletable = new CDeletable;
    // それを Delete() メンバ関数で解放します。
    pcDeletable->Delete();
    // delete します。

    return 0;
}

「この CDeletable クラスは、 delete じゃなくメンバ関数で解放するよう
になっています」
『ぎゃーっ!! メンバ関数の中で delete 使ってる、しかも this に!』

    virtual void Delete()
    {
        OutputDebugString( "delete します。\n" );
        // 自分自身を delete します。
        delete this;    ← pcDeletable の中のアドレスと同じ。
    }

「実はこんなふうに、メンバ関数の中で delete を呼び出すことができるん
です」
『これも気持ち悪い……』
「この方法のメリットは、ポリモーフィズムできること。実は、さっきの 
delete 演算子は static メンバ関数という扱いなのでポリモーフィズム
できないんです」
『げ、そうなんだ』
「でもこっちの方法なら普通のメンバ関数だから」
『 virtual 付けて仮想関数にできるからオーバーライドできる』
「そうすることで、クラスによって解放方法を変えたりできるんです。クラス
によって解放方法を変えたい場合にはこっちの方が便利かな」
『でも間違えて delete しちゃいそう……』

/*
    Preview Next Story!
*/
『な、なんか今回は異次元の世界でしたよ?』
「こういうトリッキーなプログラム、いっぱいあるよ」
『やっぱりトリッキーなんだよねやっぱり』
「でも C++ じゃ普通かも」
『なんですとー!?』
「というわけで次回」
< Version 17.23 スマートポインタ >
『につづく!』
「スマートポインタもとっても面白いよ?」
『なんだかその笑顔がハンザイシャに見えますよ?』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。