2013-04-06 135 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。尽管在对象自杀后小心不要访问成员是安全的,但让对象管理对象的其他对象的生命周期通常更为安全。