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