2013-04-06 106 views
3

我有一個類,我希望能夠設置一個標誌,說如果它是堆分配,所以它可以正確清理本身之後,而不是嘗試刪除自己,如果它是在堆棧。問題是......我似乎無法同時覆蓋new和構造函數。所以它從我的new重載設置isHeapAllocated標誌,然後到我的構造函數重置標誌。檢測堆棧或堆分配

void* String8::operator new(size_t size) 
{ 
    String8* string = (String8*)malloc(size); 
    if(string == null) 
     Exception("allocation fail : no free memory"); 
    string->isHeapAllocated = true; 
    return string; 
} 

String8::String8() 
{ 
    isHeapAllocated = false; 
} 

所以new String8()設置isHeapAllocated標誌,然後將其重置爲false。有沒有辦法做到這一點?

+1

它真的應該是調用類的職責,以刪除'String8'。 – Tushar 2013-04-06 21:03:13

+1

一個類不應該介意它是在堆棧上還是在堆上分配。該類的用戶應該執行必要的清理(如果有的話)。 – mfontanini 2013-04-06 21:04:24

+0

恕我直言,你應該使用類似boost類型的特徵來獲取關於對象的信息,而不是自己實現它。 – 2013-04-06 21:17:33

回答

3

如預期它不會工作:

new操作符返回未初始化的內存必須考慮到的構造。 你 - 正確地做 - String8* string = (String8*)malloc(size);,但*string,在這個階段還不是一個String8對象:它只是包含它的內存批量。

因此string->isHeapAllocated = true;實際上在尚未構造的對象(即UB)內設置了一個標誌。如果你以後會做一些類似於String8* ptr = new String8;的事情,在新的返回之後,你會發現這個程序不會崩潰(你寫的內存已經屬於你了,畢竟...)將調用String8 :: String8構造函數,並且成員將獨立於您在新運算符重載中執行的操作而重新設置爲「false」。

管理C++對象的慣用方式是讓誰分配來負責釋放。 (如果「誰」是堆棧,它只是按照定義來做)。

0

請注意,construtor在被分配到堆棧或堆上時被調用,並且對象無法檢測它是分配到堆棧還是堆中。

要在堆棧創建一個對象,你不使用任何內存分配函數這樣

String8 myString; 

要在你

String8 *myString = new String8(); 

注意,你必須堆創建不再使用對象後手動進行清理。

對於綁定到堆棧範圍的堆對象的使用,您可以查看由C++程序強烈使用的RAII原理(請參閱here以更好地解釋堆分配和堆棧分配的差異)。

0

不知道爲什麼你需要這個,真的。如果需要,調用者有責任調用delete,並且無論是在堆棧上還是堆上的對象上調用類的析構函數都不會有所不同......但是,也許,您正在做一些特殊目的類...以下是我的快速解答在上面。

編輯:你也應該,可能,添加自定義delete操作人員類,除非你知道全球delete調用您在您的自定義new運營商使用分配功能匹配的釋放函數。

#include <cstdlib> 
#include <iostream> 

namespace so 
{ 

class _test_ 
{ 
private: 
    static bool flag_allocation_heap; 
    bool flag_heap; 

public: 
    _test_() 
     : flag_heap(flag_allocation_heap) 
    { 
    flag_allocation_heap = 0; 
    std::cout << flag_heap << std::endl; 
    } 

    void * operator new(std::size_t _size) 
    { 
    _test_ * test_ = static_cast< _test_ * >(std::malloc(_size)); 
    flag_allocation_heap = 1; 
    return (test_); 
    } 
}; 

bool _test_::flag_allocation_heap = 0; 

} // namespace so 

int main() 
{ 

so::_test_ test_stack_; 
so::_test_ * test_memory_ = new so::_test_; 

delete test_memory_; 

return(0); 
} 

輸出:

0 
1 
1

這是一個壞主意,但這裏有一個辦法做到這一點不調用未定義的行爲。

#include <iostream> 
#include <memory> 
#include <set> 

using namespace std; 

class C { 
public: 

    void* operator new(size_t size) { 
    C* c = static_cast<C*>(::operator new(size)); 
    heap_instances.insert(c); 
    return c; 
    } 

    C() : heap_allocated(heap_instances.find(this) != heap_instances.end()) {} 

    const bool heap_allocated; 

private: 
    static set<const C*> heap_instances; 
}; 

set<const C*> C::heap_instances; 

int main(int argc, char** argv) { 
    cout << boolalpha; 

    C stack; 
    cout << stack.heap_allocated << '\n'; // false 

    C* heap_nozero = new C; 
    cout << heap_nozero->heap_allocated << '\n'; // true 
    delete heap_nozero; 

    C* heap_zero = new C(); 
    cout << heap_zero->heap_allocated << '\n'; // true 
    delete heap_zero; 
} 

當你與他們所做的,當然,你可以刪除heap_instances指針,如果你在多線程環境中運行使用更合適的容器。但是,我不會建議你真的這樣做 - 基於分配的決定行爲不是一個對象應該做的事情。

我能想到的唯一正當理由是啓用delete this。儘管在對象自殺後小心不要訪問成員是安全的,但讓對象管理對象的其他對象的生命週期通常更爲安全。