C++ Can a C++;类确定它是否为';在堆栈上还是堆上?
我有 Foo是否有一种方法能够区分:C++ Can a C++;类确定它是否为';在堆栈上还是堆上?,c++,stack,heap,C++,Stack,Heap,我有 Foo是否有一种方法能够区分: class Foo { .... } 及 我希望Foo能够根据它是在堆栈上分配还是在堆上分配来做不同的事情 编辑: 很多人问我“为什么要这样做?” 答案是: 我现在正在使用一个参考计数GC。不过,我也希望有能力运行mark&sweep。为此,我需要标记一组“根”指针——它们是堆栈上的指针。因此,对于每个类,我想知道它们是在堆栈中还是在堆中。为您的类重载new()。通过这种方式,您可以区分堆和堆栈分配,但不能区分堆栈和静态/全局分配。我不能肯定您的要求,但覆
class Foo {
....
}
及
我希望Foo能够根据它是在堆栈上分配还是在堆上分配来做不同的事情
编辑:
很多人问我“为什么要这样做?”
答案是:
我现在正在使用一个参考计数GC。不过,我也希望有能力运行mark&sweep。为此,我需要标记一组“根”指针——它们是堆栈上的指针。因此,对于每个类,我想知道它们是在堆栈中还是在堆中。为您的类重载new()。通过这种方式,您可以区分堆和堆栈分配,但不能区分堆栈和静态/全局分配。我不能肯定您的要求,但覆盖
新的
运算符可能是您试图做的事情。在C++中堆上创建对象的唯一安全方法是使用<代码>新< /Cord>运算符,可以区分堆上存在的对象与其他形式的内存。有关更多信息,请使用谷歌“重载c++中的新功能”
但是,你应该考虑是否从课堂内部区分这两种类型的记忆是必要的。如果你不小心的话,让一个对象根据其存储位置的不同而表现出不同的行为听起来就像是一个灾难 你需要问我们真正的问题(a):-)你可能很清楚为什么你认为这是必要的,但几乎肯定不是。事实上,这几乎总是个坏主意。换句话说,你为什么认为你需要这样做 我通常发现,这是因为开发人员希望根据对象的分配位置删除或不删除对象,但这通常应该留给代码的客户端,而不是代码本身
更新: 现在您已经澄清了问题中的原因,我很抱歉,您可能已经找到了您所问的有意义的少数领域之一(运行您自己的垃圾收集过程)。理想情况下,您应该覆盖所有内存分配和取消分配操作符,以跟踪从堆中创建和删除的内容 但是,我不确定截取类的new/delete是否是一个简单的问题,因为可能存在未调用
delete
的情况,而且由于标记/扫描依赖于引用计数,因此需要能够截取指针分配才能使其正常工作
你有没有想过要怎么处理
经典的例子是:
function blah() {
Foo foo* = new Foo(); // on the heap
}
不会导致删除调用
另外,您将如何检测指向实例之一的指针位于堆栈上的事实?截取new和delete可以让您存储对象本身是基于堆栈还是基于堆,但我不知道如何判断指针将被分配到何处,尤其是使用以下代码:
myobject *x = new xclass();
x = 0;
也许您需要研究一下C++的智能指针,它在使手动内存管理过时方面有很大的帮助。共享指针本身仍然会遇到循环依赖等问题,但明智地使用弱指针可以很容易地解决这一问题 在您的场景中,可能不再需要手动垃圾收集
(a) 这就是所谓的
X/Y问题
。很多时候,人们会问一个预先假定了一类解决方案的问题,而更好的方法是只描述问题,而不对最佳解决方案有任何先入之见。一种简单的方法:
myobject *x1 = new xclass(); // yes, calls new.
myobject *x2 = x; // no, it doesn't.
struct-Detect{
检测(){
int i;
检查(&i);
}
私人:
无效检查(int*i){
int j;
如果((i<&j)=((void*)本<(void*)&j))
pax提出的元问题是“你为什么要这样做”,你可能会得到一个信息更丰富的答案
现在假设您这样做是出于“一个很好的原因”(也许只是好奇),可以通过覆盖运算符new和delete来获得这种行为,但不要忘记覆盖所有12个变量,包括:
新建,删除,新建不抛出,删除不抛出,新建数组,删除数组,新建数组不抛出,删除数组不抛出,放置新建,放置删除,放置新建数组,放置删除数组
您可以做的一件事是将其放入基类并从中派生
这是一种痛苦,那么您想要什么不同的行为呢?如上所述,您需要通过重载的new操作符来控制对象的分配方式。但是,请注意两件事,第一件是“placement new”操作符,它在用户预分配的内存缓冲区内初始化您的对象;第二件是,没有任何东西可以阻止我们从简单地将任意内存缓冲区强制转换到对象类型:
struct Detect {
Detect() {
int i;
check(&i);
}
private:
void check(int *i) {
int j;
if ((i < &j) == ((void*)this < (void*)&j))
std::cout << "Stack" << std::endl;
else
std::cout << "Heap" << std::endl;
}
};
另一种方法是,大多数运行时在执行堆分配时使用的内存比要求的内存多一点。它们通常会在那里放置一些服务结构,以通过指针确定适当的释放。您可以检查运行时实现中的这些模式,尽管这会使您的代码变得不可移植、危险且不受支持马尔基尔
同样,如上所述,当您应该询问您设计此解决方案的初始问题(“为什么”)时,您实际上是在询问解决方案的详细信息(“如何”)。一种更直接、侵入性更小的方法是在内存区域映射中查找指针(例如/proc//maps
)。每个线程都有一个分配给其堆栈的区域。静态和全局变量将存在于、rodata或const段中的常量中,依此类推。答案是否定的,没有标准的/可移植的方法来做到这一点。涉及重载新运算符的黑客往往有漏洞。依赖于检查指针地址的黑客是特定于操作系统的nd堆实现特定,并且可能会随着操作系统的未来版本而改变
struct Detect {
Detect() {
int i;
check(&i);
}
private:
void check(int *i) {
int j;
if ((i < &j) == ((void*)this < (void*)&j))
std::cout << "Stack" << std::endl;
else
std::cout << "Heap" << std::endl;
}
};
char buf[0xff]; (Foo*)buf;
class blah {
Foo foo; // on the stack? Heap? Depends on where the 'blah' is allocated.
};
#include <iostream>
class A
{
public:
A()
{
int x;
asm("movq %1, %%rax;"
"cmpq %%rsp, %%rax;"
"jbe Heap;"
"movl $1,%0;"
"jmp Done;"
"Heap:"
"movl $0,%0;"
"Done:"
: "=r" (x)
: "r" (this)
);
std::cout << ( x ? " Stack " : " Heap " ) << std::endl;
}
};
class B
{
private:
A a;
};
int main()
{
A a;
A *b = new A;
A c;
B x;
B *y = new B;
return 0;
}
Stack
Heap
Stack
Stack
Heap
class CTestNEW : public CObject
{
public:
bool m_bHasToBeDeleted;
__declspec(thread) static void* m_lastAllocated;
public:
#ifdef _DEBUG
static void* operator new(size_t size, LPCSTR file, int line) { return internalNew(size, file, line); }
static void operator delete(void* pData, LPCSTR file, int line) { internalDelete(pData, file, line); }
#else
static void* operator new(size_t size) { return internalNew(size); }
static void operator delete(void* pData) { internalDelete(pData); }
#endif
public:
CTestNEW();
public:
#ifdef _DEBUG
static void* internalNew(size_t size, LPCSTR file, int line)
{
CTestNEW* ret = (CTestNEW*)::operator new(size, file, line);
m_lastAllocated = ret;
return ret;
}
static void internalDelete(void* pData, LPCSTR file, int line)
{
::operator delete(pData, file, line);
}
#else
static void* internalNew(size_t size)
{
CTestNEW* ret = (CTestNEW*)::operator new(size);
return ret;
}
static void internalDelete(void* pData)
{
::operator delete(pData);
}
#endif
};
#include "stdafx.h"
.
.
.
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
void* CTestNEW::m_lastAllocated = NULL;
CTestNEW::CTestNEW()
{
m_bHasToBeDeleted = (this == m_lastAllocated);
m_lastAllocated = NULL;
}
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
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;