2009-11-27 102 views
9

我想用自己的分配器替換新的/刪除。因此,重寫新的和刪除的位置 - 對此非常滿意。看起來像這樣...全局「放置」刪除[]

C++語言規定,對於放置刪除,您必須顯式調用〜dtor。編譯器不會爲你做。無論這是一個模板化的操作符刪除或顯式功能,如圖所示。

http://www2.research.att.com/~bs/bs_faq2.html#placement-delete

的問題是 - 我怎麼能得到這個數組工作刪除[]?我知道我需要遍歷數組並自己調用〜dtor。因此,我所需要的陣列的大小,

編輯爲了清楚

我可以存儲該信息,或從該塊大小推斷它。然而,問題是編譯器(MSVC v9)做了不同的事情,如果我使用析構函數分配一個對象數組而不使用析構函數,也就是說如果有一個dtor它將分配額外的4個字節。這是因爲用於標準delete []的編譯器需要做同樣的事情,並且可以爲delete []配對適當的代碼。

但是在我自己的「placement」中刪除[]我無法知道編譯器做了什麼,或者在編譯時安全地確定類是否有dtor。

E.g.

char buf[ 1000 ]; 

MyClass* pA = new(buf) MyClass[ 5 ]; 

這裏PA的值被buf絕對+ 4,如果存在〜MyClass的()和分配的存儲器的量是的sizeof(MyClass的)* 5 + 4。然而,如果沒有析構函數然後pA的== BUF和分配的內存量sizeof(MyClass)* 5.

所以我的問題是 - 這種行爲是一種語言標準,並在編譯器之間保持一致,還是MSVC特有的?有沒有其他人對這個問題有一個很好的解決方案?我想唯一的選擇是不使用new []並自己做這個很好的構造,但是調用代碼的語法有點不尋常..或者強迫每個類都有一個析構函數。

+1

你不越權「位置刪除」在這裏。 – 2009-11-27 16:50:26

+0

如果alignof(MyClass)== 8,我會打賭pa是_not_ buf + 4. – Bahbar 2009-11-27 16:57:37

+0

是真的,但假設我可以獲得大小,我仍然不知道編譯器是否已將它放入。如果MyClass沒有dtor pA == buf。 – Gwaredd 2009-11-27 17:40:27

回答

3

簡短的回答:

有這個用法沒有直接的支持。如果您使用不同的簽名重新載入新內容,編譯器認爲它是新的重載(而不是新的位置),並添加了自己的簿記代碼。沒有辦法(我可以找到)對編譯器說「放鬆你的記錄,並打電話給我的刪除過載符合這個簽名」 - 當調用void operator delete(void* p)void operator delete[](void* p)時,它只會插入代碼以展開記錄。

如果您使用新簽名重寫new,編譯器會讓您在新建期間出現異常時定義具有匹配簽名的刪除 - 這是它唯一使用的時間。

沒有放置刪除的意思,它不可調用,但它是在例外(什麼都不做)的情況下定義的。

龍答:

本主題提出了一些有趣的觀點:

  1. 究竟什麼呢void* operator new[](size_t sz, Allocator* a)超載?
  2. 是否有或沒有「刪除位置」。
  3. 如何以編譯器插入其簿記定稿的方式調用void operator delete[](void* p, Allocator* a)

第1點:關於重載貼裝新增功能的大量討論。鑑於編譯器插入了簿記代碼,它必須認爲void* operator new[](size_t sz, Allocator* a)聲明瞭(非放置)新的重載。它永遠不會插入新的書籍記錄代碼,因爲新的安置點是您自己處理它。

第2點:R.E. 「不存在刪除位置信息」這樣的事情,你會發現一些看起來非常喜歡(並且如此評論)的東西。 VS2k8新的標題。它只是一個存根,用於在新放置期間發生異常的情況。不過,您確實無法以有意義的方式調用展示位置刪除。

第3點:如果有辦法,我找不到它。這是問題的核心。

從實際問題的解決方案來看,它似乎是一個破產。

例如:

//intention: user provides memory pool, compiler works out how many bytes required 
//and does its own book-keeping, as it would for a void* operator new[](size_t sz) overload 
//calling syntax: CObj* pMyArr = new(pMyMemPool) CObj[20]; 
void* operator new[](size_t sz, IAlloc* pMemPool) 
{ return pMemPool->alloc(sz); } 

//problem: don't know the syntax to call this! 
//e.g. delete[](pMyMemPool) pMyArr is syntax error 
void* operator delete[](void* p, IAlloc* pMemPool) 
{ return pMemPool->free(p); } 

//nb: can be called as operator delete(pMyArr, pMyMemPool); 
//but compiler does not finish its book-keeping or call dtors for you in that case. 

注意,這種不對稱存在非數組新&刪除了。但是,因爲(經驗上)有問題的編譯器沒有額外的簿記,它可以全部工作。再一次,如果這是標準的我不知道。

void* operator new(size_t sz, IAlloc* pMemPool) 
    { return pMemPool->alloc(sz); } 


//don't know syntax to get this called by compiler! 
    void operator delete(void* p, IAlloc* pMemPool) 
    { pMemPool->free(p); } 

    //is ok though, can work around 
    template<class T> void tdelete(void* p, IAlloc* pMemPool) 
    { 
    //no problems, p points straight at object 
    p->~T(); 

    operator delete(p, pMemPool); 
    //OR just 
    pMemPool->free(p); 
    } 

    void* operator new[](size_t sz, IAlloc* pMemPool) 
    { return pMemPool->alloc(sz); } 

    //again, don't know syntax to end up here. 
    void operator delete[](void* p, IAlloc* pMemPool) 
    { pMemPool->free(p); } 

    //can't work around this time! 
    template<class T> void tarrdelete(void* p, IAlloc* pMemPool) 
    { 
    //problem 1: how many to dtor? 
    for(int i=0; i<???; ++i) 
    { reinterpret_cast<T*>(p+i)->~T(); } 
    //problem 2: p points at first element in array. this is not always the address 
    //that was allocated originally. 
    pMemPool->free(?); 

    //as already explained by OP, no way to tell if p is address allocated or 
    //address allocated+4 bytes, or something else altogether. this means no way to know what address to un-alloc or how many dtors to call. 

    } 

最後,我會說明obvs。 - 沒有擴展參數列表重載做的工作:

//sz may include extra for book-keeping 
void* operator new[](size_t sz) 
{ return GAlloc->alloc(sz); } 

//works fine, compiler handled book-keeping and p is the pointer you allocated 
void operator delete[](void* p) 
{ return GAlloc->free(p); } 

摘要:有沒有語法,將允許具有擴展的參數列表中刪除,啓用編譯器「神奇」的超載電話。或者,有沒有辦法通過重寫將參數添加到新的位置?

疑似答案:第

推論:不能從內置的完全自由新簽名的6流浪。這樣做會導致新的重載,使用編譯器生成的簿記,但無法訪問相應的刪除來放鬆記錄。

警告:您可以偏離內置簽名,但只能注入在刪除時不需要再次處理的代碼(例如,工具)。如果您遇到void* operator new(size_t s)版本的分配問題,那麼刪除仍然可以正常工作。

(實際上有些語句從調試實驗得出,並可能僅適用於MSVC8(C 1-9)。OP坐在桌子旁邊給我。)

+0

所以我搞砸了;) http://groups.google.co.uk/group/comp.lang.c++/browse_thread/thread/80753a80fa705077/f7928615131dab9d?hl=zh-CN&ie=UTF-8&oe=utf-8&q= placement + delete + group:comp.lang.c%2B%2B – Gwaredd 2009-11-28 19:26:18

+0

顯然它是「實現定義」,參見33.4&33.5 ... http://groups.google.co.uk/group/comp。答案/ browse_thread /線程/ c8dee7162c40b348/38af85c3d71454d0 HL = EN&即= UTF-8&OE = UTF-8&Q =放置+刪除+析+陣列+組:comp.lang.c%2B%2B – Gwaredd 2009-11-28 19:59:35

0

你可以看看指針在你的分配器,找出從你的簿記大小和使用的sizeof T.

4

疑問時去的專家計算元素的數量:

http://www.stroustrup.com/bs_faq2.html#placement-delete

但是我們以後如何正確刪除這些對象?沒有內置的「放置刪除」來匹配放置新的原因是沒有確保正確使用它的一般方法。 C++類型系統中的任何內容都不允許我們推斷p1指向在Arena a1中分配的對象。指向分配給任何地方的任何X的指針可以分配給p1。

鏈接的其餘部分繼續描述如何解決這種情況。

+0

是的,我閱讀了鏈接 - 這與問題中的相同?它不提供數組的解決方案,問題是沒有辦法推斷編譯器是否分配了額外的空間,所以我不知道我的實際數組在哪裏開始。這可能是一個MSVC的具體事情,以及... – Gwaredd 2009-11-27 17:30:15

+0

@Gwaredd,我想讓你仔細看看鏈接,它說,沒有這樣的事情,刪除位置,如果你想做什麼你試圖去做,你需要自己跟蹤大小。 :) – chollida 2009-11-27 22:33:49

+0

是的,我沒有非常清楚的問題:o問題是編譯器對新[]做兩件不同的事情,具體取決於dtor的存在。只是想知道如果有人有解決方案:) – Gwaredd 2009-11-28 12:24:42

1

沒有「放置刪除」這樣的術語。正如你所說的,如果你使用placement new分配一些東西,那麼當它需要釋放時,你需要手動調用析構函數,然後照顧分配給新放置的實際內存緩衝區。

但是如果不手動記錄您自己的分配大小,您試圖做的事情是不可能的。原因是「放置新」的全部要點是將分配從對象初始化中分離出來。因此,在放置新內容時,分配內存緩衝區的行爲與構建或破壞可能(或不可能)發現自己生活在緩衝區中的任何對象完全分離。因此,例如,如果您分配了一些緩衝區,如char buf[1000],然後使用placement new在該緩衝區中構建一個Foo對象的數組,那麼C++應該在哪裏存儲數組大小信息?它不會將其存儲在緩衝區中,因爲它不知道您想要對該緩衝區執行什麼操作。因此,您需要記錄每個分配的大小,然後將其與解除分配正確耦合。

+0

我沒有在問題中解釋得很清楚。我知道我需要手動調用析構函數並存儲任何我需要的信息來實現這一點。但是,編譯器會爲我生成初始化代碼。在array new []的情況下,編譯器(MSVC 9)會在類有dtor時生成不同的代碼。對於正常的刪除[],我假設編譯器生成適當的破壞代碼,但是對於放置刪除它不符合(按照C++規範),在我的刪除[]中我無法知道編譯器在一般情況下做了什麼。 ..除非我失去了一些東西? – Gwaredd 2009-11-28 12:09:36