第2章メモリ管理
- No.5 newとdeleteのペアは同じ形式に揃えよう
形式とは配列([])の有無のこと。
string *ary = new string[100]; delete []ary;
typedefを使うと、表面上配列指定の[]の有無が分からなくなるため、delete時に間違えやすいことが指摘されている。
typedef string addresslines[4]; string *ary = new addresslines; // new string[4]と等しい delete []ary; // []が必要
vectorを使えば(vector
- No.6 デストラクタでポインタメンバにdeleteを使うのを忘れないようにしよう
クラスを長期間メンテしていて、ポインタのメンバを追加したとき、コンストラクタでそのメンバについてnewするのはまず忘れないが(動かなくなるので)、deleteを忘れやすい
なお、deleteにNULLポインタを渡すことは正しい。使われてない場合NULLを入れるようにしておけば、何も考えずdeleteを発行できる。
- No.7 メモリ不足に備えよう。
bad_alloc例外があがるが、この対処としてset_new_handlerを推奨している。これはシグナルハンドラのように、グローバルに設定されるもので、newでメモリ不足が起きた場合、new handler関数が呼ばれる。
この関数では、abortする、事前に取得していたメモリを開放する、等を行なう。
重要なのはメモリを開放しリトライすることが可能であり、この場合、無限ループとなる可能性がある。
後半で、あまり便利だとは思わないが、クラスごとのnew handlerを定義する方法を説明している。これはoperator newを再定義する。
void * X::operator new(size_t size) { void *memory; new_handler prev = std::set_new_handler(my_handler); //登録 try { memory = ::operator new(size); //オリジナルのnewを呼ぶ } catch(std::bad_alloc){ std::set_new_handler(prev); //戻す throw; } std::set_new_handler(prev); //戻す return memory; }
- No.8 operator newとoperator deleteを書くときは規約を守ろう
配列でnewした場合、継承したクラスでnewした場合、等にも対応しなければならないが、これらはoperator newの引数で指定されるサイズが(期待と)異なるので、ことなった場合は標準のnewを呼べばいい、ということが書かれている。
わざわざoperator newを定義してるのに、それでいいのかよくわからない。
- No.9 普通の形式のnewを隠蔽しないようにしよう
C++ではbaseクラスとderivedクラスで同じメソッドがある場合、baseクラスのメソッドを使えなくなるようにしているらしい。バグで誤ってbaseクラスの方が参照していても気づきにくいから、という理由らしい。この仕組みにより、operator newを定義すると、普通のnewが(特殊な指定無しでは)使えなくなる。
つまり、引数付きのnewを作ったとき、引数無しの(普通の)newがエラーとなる。
- No.10 operator newを書くならoperator deleteを書こう
operator newを書く理由として、メモリ管理の効率化がある。たとえば最初のnewのときに、後のnewで返す分の領域も確保して、フリーリストのような形で管理することで、メモリ取得・開放のコストを削除しようとする場合がある。この場合、deleteもフリーリストに戻すような処理に置き換える必要がある。