C++Exceptionalリロード演算子newとdeleteの場合の注意点
5573 ワード
まず、次のコードを見て、コードに存在する問題を見つけます.
以下、上記のコードの問題について説明します.1.
また、次のコードを見てみましょう.
質問は、上記のコードがDの
コードを見てみましょう.
読者はすぐに上記のコードの問題を見ることができると信じています.問題は対応する
まずここで注意しなければならないのは
class B
{
public:
virtual ~B();
void operator delete(void*, size_t) throw();
void operator delete[](void*, size_t) throw();
void f(void*, size_t) throw();
};
class D : public B
{
public:
void operator delete (void*) throw();
void operator delete[](void*) throw();
};
以下、上記のコードの問題について説明します.1.
operator delete
がありますが、対応するoperator new
がありません.これはメモリの問題を引き起こしやすいです.2.operator delete
静的とは宣言されていない.もちろん、ここでは静的に宣言されていませんが、コンパイラはoperator delete
とoperator new
を静的な関数としてデフォルトで使用しますが、可読性のためにoperator delete
とoperator new
を明示的に静的に宣言します.3.ベースクラスにoperator delete
と宣言されたときにsize_t
のパラメータが1つ増えたことに気づきましたが、派生クラスにはありません.これで何か問題がありますか?答えは何の影響もなく、書くことも書かないこともでき、自分の好みを見ることができます.また、次のコードを見てみましょう.
B* pb1 = new D;
delete pb1;
質問は、上記のコードがDの
operator delete
に正しく呼び出されるか、答えは可能です.このとき、operator delete
が静的である以上、なぜpb 1でクラスDのoperator delete
を呼び出すことができるのかと聞かれるかもしれません.これはコンパイラが私たちのためにしてくれたことに感謝します.まずクラスBの解析関数は虚であるため,pb 1の解析関数を呼び出すとクラスDの解析関数に呼び出されるが,このときc++は解析関数にフラグを設定し,このオブジェクトが静的割当てであるといえばfalseであるが,このオブジェクトが動的割当てであればtrueとなる.このフラグがtrueの場合、c++は対応するdeleteを呼び出してメモリ領域を解放します.これはクラスDのoperator delete
に異動できる理由を説明している.もちろん、ここでクラスBの解析関数が非虚であれば、pb 1の解析関数を呼び出すとき、クラスDは呼び出されません.このとき、クラスDが正しく解析されていないだけでなく、対応するoperator delete
の呼び出しも間違っています.コードを見てみましょう.
class ShareMemery
{
public:
static void* Allocate(size_t s)
{
return OsSpecificShareMemAllocation(s);
}
static void Deallocate(void *p)
{
OsSpecificShareMemDeallocation(p);
}
};
class Y
{
public:
static void* operator new(size_t s,
SharedMemery& m) throw(bad_alloc)
{
return m.Allocate(s);
}
}
SharedMemery shared;
...
new (shared) Y; // if Y::Y() throws, memory is leaked
読者はすぐに上記のコードの問題を見ることができると信じています.問題は対応する
operator delete
が欠けていることです.以下、以上のクラスを改善します.class Y
{
public:
static void* operator new(size_t s,
SharedMemory& m) throw(bad_alloc)
{
return m.Allocate(s);
}
static void operator delete(void* p, SharedMemory& m) throw()
{
return m.Deallocate(p);
}
static void operator delete(void* p) throw()
{
return SharedMemory::Deallocate(p);
}
}
まずここで注意しなければならないのは
operator delete
が2つ提供されていることですが、なぜそうなのでしょうか.第一に、必ずoperator new
に対応するoperator delete
が必要です.ここでの対応は、第一のパラメータを除いて、残りのパラメータは一つ一つ対応することができます.このようにペアを組んで現れるのは、メモリの漏洩を避けるためです.自分たちのoperator new
を呼び出すと、オブジェクトの初期化に異常が発生し、このときc++はペアリングのoperator delete
を呼び出してメモリを解放し、コンパイラに参加してペアリングのoperator delete
が見つからないと仮定すると、このメモリは漏れてしまいます.また、なぜ第2の形式のoperator delete
が提供されるのか、これは私たちがdelete Y
を呼び出すとき、このときペアリングを呼び出すことはありません.ここには伝参がなく、伝参もできないので、第2の形式のoperator delete
が提供されるからです.以上のように、カスタマイズされたoperator new
とoperator delete
は必ずペアで現れなければならない.そうしないと、メモリの漏洩やプログラムのクラッシュなどの問題が発生する.