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:

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

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

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

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

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

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

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

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

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

    ReplyDelete

You can use some HTML elements, such as <b>, <i>, <a>, also, some characters need to be entity referenced such as <, > and & Your comment may need to be confirmed by blog author. Your comment will be published under GFDL 1.3 or later license with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.