2010-07-02

デフォルトのoperator newの実装例

規格通りにデフォルトの挙動を実装してみた。

なんでget_new_hander()がないのだろう。

placement deleteの呼ばれる条件というのが、極めてレアだ。placement newに対応する同じ引数のplacement deleteは、ストレージの確保には成功したが、オブジェクトのコンストラクターが例外を投げた場合に呼ばれる。

結局、operator newというのは、new式のオーバーロードではなく、new式で使われるallocation functionのオーバーロードだというのが、問題をややこしくしている。

この際、new式の挙動を詳しく説明すべきだろう。

void * operator new ( std::size_t size ) throw( std::bad_alloc )
{
    if ( size == 0 ) size = 1 ;

    // Executes a loop: Within the loop,
    while( true )
    {
        // the function first attempts to allocate the requested storage.
        void * ptr = std::malloc( size ) ;

        // if the attempt is successful
        if ( ptr != nullptr )
        {// Returns a pointer to the allocated storage
            return ptr ;
        }

        // Otherwise, 
        std::new_handler handler = std::set_new_handler( nullptr ) ;
        std::set_new_handler( handler ) ;

        // if the argument in the most recent call to set_new_handler() was a null pointer,
        if ( handler == nullptr )
        {// throws bad_alloc.
            throw std::bad_alloc() ;
        }

        // Otherwise, the function calls the current new_handler function.
        handler() ;
        // If the called function returns, the loop repeats.
    }
}

void * operator new( std::size_t size, const std::nothrow_t & ) throw()
{
    try {
        // Calls operator new(size).
        // If the call returns normally, returns the result of that call. 
        return operator new( size ) ;
    } catch( ... )
    {
        // Otherwise, returns a null pointer.
        return nullptr ;
    }
}

void operator delete( void * ptr ) throw()
{
    // If ptr is null, does nothing.(std::free does nothing too)
    // Otherwise, reclaims the storage allocated by the earlier call to operator new.
    std::free( ptr ) ;
}

void operator delete(void * ptr, const std::nothrow_t & ) throw()
{
        // calls operator delete(ptr).
        operator delete( ptr ) ;
}

6 comments:

melpon said...

malloc は 0 を渡した場合の動作が未定義だった気が。
Effective C++ には if (size == 0) size = 1; というコードが書かれてますね。

萌ゑ said...

operator newは実行中に例外が投げられると
自動的にoperator deleteを呼び出すんですよね。

ところが現在の有名な処理系でもこの動作に未だに準拠していない物があります。信じられない事ですが。

江添亮 said...

その辺なんですが。
まず、オーバーロードされたoperator newが呼び出すのではなくて、new式が呼び出します。
呼び出す状況というのは、ストレージの確保はできたけれど、初期化中にコンストラクターが例外を投げた場合です。
規格では、
"it may release storage by calling a deallocation function"
とあって、必ず呼び出さなければならないとは書いていません。

とはいえ、呼び出してくれないと、安心してコンストラクターから例外を投げられないんですけどね。

Anonymous said...

new はともかく、この delete の実装はないでしょ、、、デストラクタの呼び出しの無いdeleteなんて、、、アンタ、ホントに標準化委員会の人なの、、、

江添亮 said...

デストラクターを呼び出すのは、delete式の役目です。
delete演算子は、ストレージの解放を行ないます。

new演算子とdelete演算子のオーバーロードは、new式とdelete式のストレージの確保解放の部分だけをオーバーロードするので、理解しづらいのです。

江添亮 said...

ちなみに規格上は、delete演算子のオーバーロードは、解放関数(deallocation function)となっています。