4

我有一个类,我希望能够设置一个标志,说明它是否是堆分配的,这样它就可以在自己之后正确清理,如果它在堆栈上,就不会尝试删除自己。问题是......我似乎无法同时覆盖两者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

4 回答 4

3

它不会按预期工作:

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

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

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

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

于 2013-04-06T21:21:20.503 回答
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. 如果在对象自杀后小心不要访问成员,这是安全的,但让对象管理其他对象的生命周期通常更明智。

于 2013-04-06T21:43:28.177 回答
0

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

要在堆栈上创建一个对象,您不要使用任何像这样的内存分配函数

String8 myString;

在你做的堆上创建它

String8 *myString = new String8();

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

对于绑定到堆栈范围的堆对象的使用,您可以查看 C++ 程序大量使用的 RAII 原则(有关堆分配和堆栈分配区别的更好解释,请参见此处)。

于 2013-04-06T21:05:02.517 回答
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
于 2013-04-06T21:35:45.053 回答