C++Exceptionalリロード演算子newとdeleteの場合の注意点

5573 ワード

まず、次のコードを見て、コードに存在する問題を見つけます.
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 deleteoperator newを静的な関数としてデフォルトで使用しますが、可読性のためにoperator deleteoperator 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 newoperator deleteは必ずペアで現れなければならない.そうしないと、メモリの漏洩やプログラムのクラッシュなどの問題が発生する.