簡體   English   中英

C ++中xmalloc的正確模擬

[英]Right analogue of xmalloc in C++

兩個簡單的問題:在普通C我們經常使用xmalloc ,它是一個分配或中止例程。 我用C ++實現。 這是正確的無異常實現嗎?

template <typename T>
T *xnew(const size_t n)
{
    T *p = new (std::nothrow) T[n];
    if (p == nullptr)
    {
        cerr << "Not enough memory\n";
        abort();
    }
    return p;
}

int main()
{
    int *p = xnew<int>(5000000000LL);
}

第二個問題,如果刪除<int>xnew<int>(5000000000LL); 調用時,盡管返回類型int *仍然存在,但是編譯器(g ++ 4.7.2) 不能再推斷 [T = int] 這是為什么?

編輯:使用new版本時,即使沒有拋出異常也不會產生任何開銷嗎? 當不是絕對必要時,我真的不想使用任何異常。

我看不出為什么這是必要的。 如果無法分配內存,則new將拋出std::bad_alloc 如果不處理異常,則將導致對std::terminate的調用,該調用將有效地結束程序並具有與xmalloc相同的行為。

當然,當您的編譯器未實現異常時,這種情況會改變。

第二個問題,如果刪除<int>xnew<int>(5000000000LL); 調用時,盡管返回類型int *仍然存在,但是編譯器(g ++ 4.7.2)不能再推斷[T = int]。 這是為什么?

僅從函數調用中參數表達式的類型推導函數模板參數。 由於T不會以任何方式出現在函數參數中,因此無法推導它。

使用函數調用的返回值所做的操作永遠不會影響C ++中的模板參數推導。 如果您寫int *p = some_function(5000000000LL); 然后int* 不一定的返回類型some_function ,這是一個類型,編譯器將嘗試的返回類型轉換some_function

因此,編譯器無法推斷int的最接近原因是該標准禁止使用int (至少在沒有診斷的情況下)。 最終的原因是C ++的設計人員(最初可能是Stroustrup)想要限制演繹時要考慮的事情,以保持規則(如果不是簡單的話)至少可以被凡人所理解。

C ++中有一條規則,即子表達式的類型僅取決於子表達式本身,而不取決於周圍的表達式。 而且AFAIK僅有一個例外,即函數指針或成員函數指針不明確時:

void foo();
void foo(int);

void (*pfoo1)() = &foo; // &foo evaluates to a pointer to the void overload
void (*pfoo2)(int) = &foo; // &foo evaluates to a pointer to the int overload
void (*pfoo3)() = (void(*)(int))&foo; // &foo evaluates to the int overload, but doesn't convert to the type of pfoo3 so the line fails.

不能保證此代碼是100%安全的,因為operator<<() 可能會拋出 實際上,這不是一個普遍的情況,因為拋出將滿足一些罕見條件:

  1. std::cerrexceptions()掩碼中設置了badbit (默認情況下不是)
  2. 輸出期間引發異常

在這種情況下,異常將被重新拋出並且內存將泄漏。

關於從模板函數調用表達式中刪除<int> -當然,它將不起作用。 編譯器只能從調用表達式本身推斷模板參數類型,而不能從將要分配的左值類型推斷模板參數類型。 因此,您要自動推導的模板參數應該是函數參數,而不是返回類型:

template <class T> T f1();
template <class T> T f2(T);

int a = f1();   // Will not compile, shall be f1<int>();
int b = f2(42); // OK

異常開銷實際上取決於實現。 我相信現代編譯器足夠聰明,可以避免此類開銷,但是請確保與平台一起檢查。

如果您想避免引發new異常(無論出於何種原因-也許您正在不支持異常的平台(例如某些嵌入式平台)上工作),則可以提供new_handlernew無法分配的new_handler下中止程序記憶:

#include <stdlib.h>

#include <iostream>
#include <new>

namespace {
    void new_handler_abort()
    {
        std::cerr << "Not enough memory\n";
        abort();
    }

    struct new_handler_abort_installer {

        new_handler_abort_installer() {
            std::set_new_handler(new_handler_abort);
        }

    };


    // a statically allocated object that does nothing but install the
    //  new_handler_abort() function as the new_handler

    new_handler_abort_installer install_new_handler_abort;
}

僅將此源文件包含在您的程序中會安裝一個new_handler ,它將在程序是new時中止該程序,從而無法分配內存。

然而:

  • 何時完成此初始化並不確定(除了在調用main()之前會發生)。 因此,如果在main()之前遇到內存問題,它可能無法完全滿足您的要求。
  • 編譯器還可以添加代碼來支持異常處理,而對於一些編譯器,其包括在每次調用發生代碼operator new所以有可能仍然是架空的一些少量的花費來處理決不會發生異常(較新的編譯器可避免通過使用表驅動的堆棧展開來避免這種開銷,從而避免了必須運行代碼以在每次調用時設置異常的情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM