Item 51:newとdeleteを書くときは慣例に従ってください
Item 51: Adhere to convention when writing new and delete.
Item 50は、
外部operator new
Item 49は、戻り値を与えるのは簡単です.メモリが十分である場合、申請したメモリアドレスを返します.メモリが不足している場合、Item 49に記載されている規則に従って空または放出 失敗するたびに「new handler」を呼び出し、申請メモリを繰り返すのは容易ではありません.「new handler」が空の場合にのみ例外を放出します. 申請サイズがゼロの場合も合法的なポインタを返さなければならない.申請サイズがゼロのスペースを許可することは、確かにプログラミングに便利です.
上記の目標を考慮すると、1つの非メンバー関数の 2回
メンバーoperator new
リロード
もちろん、あなたが書いた
サブクラス継承
上のコードはチェックしていません
ここでは、対象の大きさがわかりません.継承が発生した場合
外部operator delete
同様に、外部(非メンバー)の
メンバーoperator delete
メンバー関数のdeleteも簡単ですが、他の
上のチェックは
実際
本住所:http://harttle.com/2015/09/20/effective-cpp-51.html
Item 50は、
new
とdelete
をカスタマイズする方法を紹介していますが、あなたが従わなければならない慣例を説明していません.これらの慣例の中には直感的ではないものもあります.だから、覚えておく必要があります.operator new
リソースを無限ループで取得する必要があり、取得できなければ「new handler」を呼び出し、「new handler」が存在しない場合は異常を投げ出すべきである.operator new
処理すべきsize == 0
の場合.operator delete
空のポインタと互換性があるべきである.operator new/delete
メンバー関数としてsize > sizeof(Base)
を扱うべき場合(継承が存在するため).外部operator new
Item 49は、
operator new
をクラスのメンバー関数として再ロードする方法を示しています.ここではまず、外部(非メンバー関数)のoperator new
:operator new
を実装する方法を見てみましょう.メモリが不足している場合は「new handler」を呼び出し、申請サイズが0のメモリを要求する場合も正常に実行し、グローバルな(「normal form」)new
を隠すことを避けることができます.bad_alloc
異常が返される.上記の目標を考慮すると、1つの非メンバー関数の
operator new
は、概ね以下のように実現される.void * operator new(std::size_t size) throw(std::bad_alloc){
if(size == 0) size = 1;
while(true){
//
void *p = malloc(size);
//
if(p) return p;
// , new handler
new_handler h = set_new_handler(0);
set_new_handler(h);
if(h) (*h)();
else throw bad_alloc();
}
}
size == 0
の場合、申請サイズは1
とあまり適切ではありませんが、非常に簡単で正常に動作します.ましてや、大きさが0の空間をよく申請しないでしょう.set_new_handler
呼び出しグローバル「new handler」を空に設定してから設定します.これは、「new handler」を直接取得できないため、マルチスレッド環境ではロックが必要です.while(true)
は、これがデッドサイクルである可能性があることを意味する.だからItem 49は、「new handler」がより多くのメモリを解放するか、新しい「new handler」をインストールするか、もしあなたが無駄な「new handler」を実現したら、ここはデッドサイクルです.メンバーoperator new
リロード
operator new
は、通常、サブクラスに使用されるのではなく、特定のクラスに対して動的メモリ管理を最適化するためにメンバー関数として使用されます.Base::operator new()
を実装する場合、オブジェクトサイズがsizeof(Base)
であることに基づいてメモリ管理の最適化が行われるからである.もちろん、あなたが書いた
Base::operator new
はclassとそのサブクラス全体に通用する場合がありますが、このルールは適用されません.class Base{
public:
static void* operator new(std::size_t size) throw(std::bad_alloc);
};
class Derived: public Base{...};
Derived *p = new Derived; // Base::operator new !
サブクラス継承
Base::operator new()
の後、現在のオブジェクトは仮定のサイズではないため、この方法は現在のオブジェクトのメモリを管理するのに適していません.パラメータBase::operator new
はsize
で判断でき、サイズがsizeof(Base)
でない場合、グローバルのnew
を呼び出す.void *Base::operator new(std::size_t size) throw(std::bad_alloc){
if(size != sizeof(Base)) return ::operator new(size);
...
}
上のコードはチェックしていません
size == 0
!これはC++の不思議な場所で、大きさが0の独立したオブジェクトがchar
(Item 39参照)挿入されるので、sizeof(Base)
は永遠に0ではないので、size == 0
の場合は::operator new(size)
に渡して処理しました.ここでは、
operator new[]
と同じパラメータと戻り値を持つoperator new
について説明します.これらのオブジェクトがいくつかあると仮定しないでください.また、各オブジェクトのサイズがいくらなのか、まだ存在しないオブジェクトを操作しないでください.理由:size
が必ずしもsizeof(Base)
に等しいとは限らないことも述べた.size
実パラメータの値は、これらのオブジェクトのサイズの和よりも大きい場合があります.Item 16に記載されているため、配列のサイズも格納する必要がある場合があります.外部operator delete
new
よりもdelete
を実現するルールは簡単です.唯一注意しなければならないのはC++がdelete
一つNULL
が常に安全であることを保証しているので、この慣例を尊重すればいいです.同様に、外部(非メンバー)の
delete
:void operator delete(void *rawMem) throw(){
if(rawMem == 0) return;
//
}
メンバーoperator delete
メンバー関数のdeleteも簡単ですが、他の
new
の申請を転送した場合は、size
も他のdelete
の申請を転送する必要があります.class Base{
public:
static void * operator new(std::size_t size) throw(std::bad_alloc);
static void operator delete(void *rawMem, std::size_t size) throw();
};
void Base::operator delete(void *rawMem, std::size_t size) throw(){
if(rawMem == 0) return; //
if(size != sizeof(Base)){
::operator delete(rawMem);
}
//
}
上のチェックは
size
が空、rawMem
が空ではありません.実際
size
実パラメータの値は、呼び出し元のタイプによって導出されます(虚析構造関数がなければ):Base *p = new Derived; // Base::~Base
delete p; // `delete(void *rawMem, std::size_t size)` `size == sizeof(Base)`。
size
がBase::~Base()
と宣言された場合、上記のvirtual
は正しいsize
である.これも,Item 7が構造関数をsizeof(Derived)
と宣言しなければならない理由である.本住所:http://harttle.com/2015/09/20/effective-cpp-51.html