一个C++类能否确定它是在堆上还是栈上?

47

我有

class Foo {
....
}
有没有一种方法让Foo能够分离出:
function blah() {
  Foo foo; // on the stack
}

并且

function blah() {
  Foo foo* = new Foo(); // on the heap
}

我希望根据Foo对象是在堆上还是栈上分别实现不同的功能。

编辑:

很多人问我“为什么这样做?”

答案是:

我现在正在使用引用计数的GC。但是,我也想能够运行标记和清除算法。为此,我需要标记一组“根”指针--这些指针位于堆栈上。因此,对于每个类,我想知道它们是在堆上还是在栈上。


2
分离出来,例如?在我看来,静态分配是在堆栈上完成的,而像“new”这样的分配将在堆上完成。 - user59634
3
为什么你需要将它们区分开来,有什么用途? - Georg Fritzsche
11
这个问题能被回答吗?无论那个人是否知道自己在做什么,对于那些确实需要它的人来说可能很有用。 - Matt Joiner
1
目瞪口呆地转动眼睛:-( - Martin York
3
无法进行可移植性的操作,即使能够进行也不会提供有用的信息。如果您认为自己需要这样做,几乎可以肯定是错误的。 - JoeG
显示剩余6条评论
15个回答

1

有一个解决方案,但它需要使用继承。请参考Meyers的《Effective C++》中的第27条。

编辑:
Meyers的建议在Ron van der Wal撰写的一篇文章中总结,这篇文章是Meyers自己在他的博客中链接的(在这篇文章中):

Tracking heap based objects

As an alternative to the global variable approach, Meyers presents a HeapTracked class that uses a list to keep track of the addresses of class instances allocated off the heap, then uses this information to determine if a particular object resides on the heap. The implementation goes like this:

class HeapTracked {
  // Class-global list of allocated addresses
  typedef const void *RawAddress;
  static list<RawAddress> addresses;
public:
  // Nested exception class
  class MissingAddress {};

  // Virtual destructor to allow dynamic_cast<>; pure to make
  // class HeapTracked abstract.
  virtual ~HeapTracked()=0;

  // Overloaded operator new and delete
  static void *operator new(size_t sz)
  {
    void *ptr=::operator new(sz);
    addresses.push_front(ptr);
    return ptr;
  }

  static void operator delete(void *ptr)
  {
    // Remove ‘ptr’ from ‘addresses’
    list<RawAddress>::iterator it=find(addresses.begin(),

    addresses.end(), ptr);
    if (it !=addresses.end()) {
      addresses.erase(it);
      ::operator delete(ptr);
    } else
      throw MissingAddress();
  }

  // Heap check for specific object
  bool isOnHeap() const
  {
    // Use dynamic cast to get start of object block
    RawAddress ptr=dynamic_cast<RawAddress>(this);
    // See if it’s in ‘addresses’
    return find(addresses.begin(), addresses.end(), ptr) !=
      addresses.end();
  }
};

// Meyers omitted first HeapTracked:: qualifier...
list<HeapTracked::RawAddress> HeapTracked::addresses; 

原文中还有更多内容可供阅读:Ron van der Wal对该建议进行了评论,然后演示了其他替代的堆跟踪方法。


1
为您的类重载new()。这样,您将能够区分堆和栈分配,但无法区分栈和静态/全局分配。

2
当你的类的实例是另一个类的非静态成员时,这也会带来一个免费的麻烦。 - user1203803

1
我建议使用智能指针。按设计,类应该具有关于类的数据和信息。簿记任务应该委托给类外部。
重载new和delete可能会导致比你想象的更多的漏洞。

1
为了回答你的问题,一个可靠的方法(假设你的应用程序没有使用多个线程),假设除你的智能指针之外的所有东西都不包含在堆中:
-> 重载new,以便可以存储分配的所有块的列表,并记录每个块的大小。 -> 在智能指针的构造函数中,搜索此指针所属的块。如果不在任何块中,则可以说它在“堆栈”上(实际上,这意味着它不由您管理)。否则,您就知道了指针的分配位置和时间(如果您想查找孤立的指针并懒惰地释放内存,或者像那样的事情..)。 它不取决于体系结构。

这是正确的想法,但您可能还需要担心标准分配器以及new运算符。如果您的类包含一个向量,您需要知道其存储也是被追踪的。标准分配器使用::operator new,因此您可以重新定义它并完成操作。 - Charphacy
你不需要为第三方组件进行任何重载操作。 - Sam Ginrich

0

请查看此程序:http://alumni.cs.ucr.edu/~saha/stuff/memaddr.html。通过几个转换,它会输出:

        Address of main: 0x401090
        Address of afunc: 0x401204
Stack Locations:
        Stack level 1: address of stack_var: 0x28ac34
        Stack level 2: address of stack_var: 0x28ac14
        Start of alloca()'ed array: 0x28ac20
        End of alloca()'ed array: 0x28ac3f
Data Locations:
        Address of data_var: 0x402000
BSS Locations:
        Address of bss_var: 0x403000
Heap Locations:
        Initial end of heap: 0x20050000
        New end of heap: 0x20050020
        Final end of heap: 0x20050010

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接