检测堆栈或堆分配

时间:2013-04-06 21:01:38

标签: c++ memory-management constructor new-operator

我有一个类,我希望能够设置一个标志,如果它是堆分配的,那么它可以在自己之后正确清理,如果它在堆栈上则不会尝试删除它自己。问题是......我似乎无法同时覆盖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。有没有办法做到这一点?

4 个答案:

答案 0 :(得分:3)

它不会按预期工作:

new运算符返回要给构造函数的单元化内存。 你 - 正确地做String8* string = (String8*)malloc(size);,但是*string,在这个阶段还不是String8对象:它只是包含它的内存批量。

所以string->isHeapAllocated = true;实际上在一个尚未构造的对象(即UB)中设置了一个标志。

承认这一点不会影响操作系统进程,因此程序不会崩溃(你写的内存已经属于你了,毕竟......),之后你会做String8* ptr = new String8;之类的事情。在返回新的返回值时,将调用String8 :: String8构造函数,并且该成员将根据您在新运算符重载中的操作独立地设置回“false”。

管理C ++对象的惯用方法是让谁分配负责解除分配。 (如果“谁”是堆栈,它只是按照定义那样做。)

答案 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。虽然如果你在对象自杀后小心不要访问成员,这是安全的,但让对象管理其他对象的生命周期通常更为安全。

答案 2 :(得分:0)

请注意,如果construtor在堆栈或堆上分配,则会调用construtor,并且对象无法检测它是在堆栈上还是在堆中分配。

要在堆栈中创建对象,请不要使用任何内存分配函数,例如

String8 myString;

要在堆上创建

String8 *myString = new String8();

请注意,在不再使用该对象后,您必须手动进行清理。

对于绑定到堆栈范围的Heap对象的使用,您可以查看由c ++程序强烈使用的RAII原则(请参阅here以更好地解释堆分配和堆栈分配的差异)。 / p>

答案 3 :(得分:0)

不确定为什么你需要这个,真的。调用者有责任在需要时调用delete,并且无论是在栈上还是在堆上调用对象,你的类的析构函数都不应该是不同的......但是,也许,你正在做一些特殊目的类...这里是我的快速接受。

编辑:您可能也应该向您的班级添加自定义delete运算符,除非您知道全局delete调用与您在自定义{{1}中使用的分配函数匹配的释放函数运算符。

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 );
}