示例:
bool isHeapPtr(void* ptr)
{
//...
}
int iStack = 35;
int *ptrStack = &iStack;
bool isHeapPointer1 = isHeapPtr(ptrStack); // Should be false
bool isHeapPointer2 = isHeapPtr(new int(5)); // Should be true
/* I know... it is a memory leak */
如果我在一个类中有一个成员指针,我不知道指向对象是否是新分配的。然后我应该使用这样的实用程序来知道我是否必须delete
指针。
可是:
我的设计还没有制作完成。所以,我会按照我delete
的方式编程。我要避免垃圾编程
答案 0 :(得分:30)
没有办法做到这一点 - 如果你需要这样做,你的设计就会出现问题。讨论了为什么你不能在More Effective C++中执行此操作。
答案 1 :(得分:15)
在一般情况下,你很运气,我很害怕 - 因为指针可以有任何价值,所以没有办法区分它们。如果您了解堆栈的起始地址和大小(例如,从嵌入式操作系统中的TCB),您可能能够做到这一点。类似的东西:
stackBase = myTCB->stackBase;
stackSize = myTCB->stackSize;
if ((ptrStack < stackBase) && (ptrStack > (stackBase - stackSize)))
isStackPointer1 = TRUE;
答案 2 :(得分:12)
我能想到的唯一“好”解决方案是为该类重载operator new
并跟踪它。像这样的东西(脑编译代码):
class T {
public:
void *operator new(size_t n) {
void *p = ::operator new(n);
heap_track().insert(p);
return p;
}
void operator delete(void* p) {
heap_track().erase(p);
::operator delete(p);
}
private:
// a function to avoid static initialization order fiasco
static std::set<void*>& heap_track() {
static std::set<void*> s_;
return s_;
}
public:
static bool is_heap(void *p) {
return heap_track().find(p) != heap_track().end();
}
};
然后你可以做这样的事情:
T *x = new X;
if(T::is_heap(x)) {
delete x;
}
但是,我会建议不要让你能够询问是否在堆上分配了某些东西。
答案 3 :(得分:8)
好吧,拿出你的汇编程序书,并将指针的地址与堆栈指针进行比较:
int64_t x = 0;
asm("movq %%rsp, %0;" : "=r" (x) );
if ( myPtr < x ) {
...in heap...
}
现在x将包含您需要将指针与之比较的地址。请注意,它将不用于在另一个线程中分配的内存,因为它将拥有自己的堆栈。
答案 4 :(得分:5)
在这里,适用于MSVC:
#define isheap(x, res) { \
void* vesp, *vebp; \
_asm {mov vesp, esp}; \
_asm {mov vebp, ebp}; \
res = !(x < vebp && x >= vesp); }
int si;
void func()
{
int i;
bool b1;
bool b2;
isheap(&i, b1);
isheap(&si, b2);
return;
}
它有点难看,但有效。仅适用于局部变量。如果从调用函数传递堆栈指针,则此宏将返回true(表示它是堆)
答案 5 :(得分:3)
在主流操作系统中,堆栈从顶部开始增长,而堆从底部开始增长。因此,可能启发式地检查地址是否超出了大值,对于某些“大”的定义。例如,以下内容适用于我的64位Linux系统:
#include <iostream>
bool isHeapPtr(const void* ptr) {
return reinterpret_cast<unsigned long long int>(ptr) < 0xffffffffull;
}
int main() {
int iStack = 35;
int *ptrStack = &iStack;
std::cout << isHeapPtr(ptrStack) << std::endl;
std::cout << isHeapPtr(new int(5)) << std::endl;
}
请注意,这是一个粗略的启发式方法,可能很有趣,但不适合生产代码。
答案 6 :(得分:3)
首先,你为什么要知道这个?你想解决什么真正的问题?
我知道做出这种决定的唯一方法就是超载全球operator new
和operator delete
。然后你可以询问你的内存管理器指针是否属于它(堆)(堆栈或全局数据)。
答案 7 :(得分:3)
即使您可以确定指针是在一个特定堆上还是在一个特定堆栈上,一个应用程序可以有多个堆栈和多个堆栈。
根据询问的原因,每个容器对于它是否“拥有”它所持有的指针都有严格的政策是非常重要的。毕竟,即使这些指针指向堆分配的内存,其他一些代码也可能具有相同指针的副本。虽然可以转让所有权,但每个指针一次应该有一个“所有者”。所有者负责破坏。
在极少数情况下,容器可以跟踪拥有和非拥有指针(使用标志或单独存储)。但是,大多数情况下,为任何可以保存指针的对象设置一个明确的策略更简单。例如,大多数智能指针始终拥有其容器实际指针。
当然智能指针在这里很重要 - 如果你想要一个所有权跟踪指针,我相信你可以找到或写一个智能指针类型来抽象那些麻烦。
答案 8 :(得分:2)
尽管声称相反,但显然有可能以平台相关的方式做你想做的事。然而,仅仅因为某些事情是可能的,这并不会自动成为一个好主意。一个简单的堆栈规则==没有删除,否则==删除不太可能正常工作。
一种更常见的方式是说如果我分配了一个缓冲区,那么我必须将其删除,如果程序传递给我一个缓冲区,则删除它不是我的责任。
e.g。
class CSomething
{
public:
CSomething()
: m_pBuffer(new char[128])
, m_bDeleteBuffer(true)
{
}
CSomething(const char *pBuffer)
: m_pBuffer(pBuffer)
, m_bDeleteBuffer(false)
{
}
~CSomething()
{
if (m_bDeleteBuffer)
delete [] m_pBuffer;
}
private:
const char *m_pBuffer;
bool m_bDeleteBuffer;
};
答案 9 :(得分:2)
你正在努力做到这一点。澄清您的设计,以便明确谁“拥有”数据并让该代码处理其生命周期。
答案 10 :(得分:1)
这是使用TIP在Windows中执行此操作的通用方法:
bool isStack(void* x)
{
void* btn, *top;
_asm {
mov eax, FS:[0x08]
mov btn, eax
mov eax, FS:[0x04]
mov top, eax
}
return x < top && x > btn;
}
void func()
{
int i;
bool b1;
bool b2;
b1 = isStack(&i);
b2 = isStack(&si);
return;
}
答案 11 :(得分:0)
我知道半可靠地执行此操作的唯一方法是,如果您可以为需要执行此操作的类型重载operator new
。不幸的是,那里有一些重大的陷阱,我不记得它们是什么。
我确实知道一个缺陷就是没有直接分配就可以在堆上。例如:
class A {
int data;
};
class B {
public:
A *giveMeAnA() { return &anA; }
int data;
A anA;
};
void foo()
{
B *b = new B;
A *a = b->giveMeAnA();
}
在上面的代码中,a
中的foo
结尾有一个指向堆上没有用new
分配的对象的指针。如果您的问题确实是“如何知道我是否可以在此指针上调用delete
”。重载operator new
做一些棘手的事可能会帮助你回答这个问题。我仍然认为,如果你不得不提出这个问题,那你就做错了。
答案 12 :(得分:0)
你怎么能不知道是否有堆分配的东西?您应该将软件设计为具有单点分配。
除非你在嵌入式设备中做一些真正奇特的东西或深入定制内核,否则我觉得不需要它。
查看此代码(为了示例,没有错误检查):
class A
{
int *mysweetptr;
A()
{
mysweetptr = 0; //always 0 when unalloc'd
}
void doit()
{
if( ! mysweetptr)
{
mysweetptr = new int; //now has non-null value
}
}
void undoit()
{
if(mysweetptr)
{
delete mysweetptr;
mysweetptr = 0; //notice that we reset it to 0.
}
}
bool doihaveit()
{
if(mysweetptr)
return true;
else
return false;
}
~A()
{
undoit();
}
};
特别注意我使用null值来确定指针是否已被分配,或者我是否需要删除它。
答案 13 :(得分:0)
你的设计不应该依赖于确定这些信息(正如其他人所指出的那样,这是不可能的)。相反,您的类应该明确定义它在构造函数或方法中所使用的指针的所有权。如果你的类拥有这些指针的所有权,那么传递指向堆栈或全局的指针是不正确的行为,你应该在知道不正确的客户端代码可能崩溃的情况下删除它。如果您的班级没有取得所有权,则不应该删除指针。