Can a C++ class determine whether it's on the

2019-01-22 07:01发布

I have

class Foo {
....
}

Is there a way for Foo to be able to separate out:

function blah() {
  Foo foo; // on the stack
}

and

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

I want Foo to be able to do different things depending on whether it's allocated on the Stack or the Heap.

Edit:

Alof of people have asked me "why do this?"

The answer:

I'm using a ref-counted GC right now. However, I want to have ability to run mark & sweep too. For this, I need to tag a set of "root" pointers -- these are the pointers on the stack. Thus, for each class, I'd like to know whether they're in the stack or in the heap.

标签: c++ stack heap
15条回答
别忘想泡老子
2楼-- · 2019-01-22 07:06

I am not positive what you are asking, but overriding the new operator may be what you are trying to do. As the only safe way to create an object on the heap in C++ is to use the new operator, you can differentiate between objects that exist on the heap versus other forms of memory. Google "overloading new in c++" for more information.

You should, however, consider if differentiating between the two types of memory is really necessary from inside the class. Having an object behave differently depending upon where it is stored sounds like a recipe for disaster if you are not careful!

查看更多
乱世女痞
3楼-- · 2019-01-22 07:07

Take a look at the program here: http://alumni.cs.ucr.edu/~saha/stuff/memaddr.html. With a few casts, it ouputs:

        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
查看更多
在下西门庆
4楼-- · 2019-01-22 07:08

As mentioned above, you need to control how your object is allocated through overloaded new operator. Watch out for two things however, first the 'placement new' operator that initializes your object inside the memory buffer preallocated by user; second, nothing stops the user from simply casting arbitrary memory buffer into your object type:

char buf[0xff]; (Foo*)buf;

Another way is the fact that most runtimes use a bit more memory than asked when doing heap allocations. They usually place some service structure there to identify proper deallocations by pointer. You could inspect your runtime implementation for these patterns, although it will make your code really unportable, dangerous and unsupportable overkill.

Again, as mentioned above, you really are asking for solution details ("how") when you should ask about the initial problem you devised this solution for ("why").

查看更多
贼婆χ
5楼-- · 2019-01-22 07:11

Nope, it can't be done reliably or sensibly.

You may be able to detect when an object is allocated with new by overloading new.

But then what if the object is constructed as a class member, and the owning class is allocated on the heap?

Here's a third code example to add to the two you've got:

class blah {
  Foo foo; // on the stack? Heap? Depends on where the 'blah' is allocated.
};

What about static/global objects? How would you tell them apart from stack/heap ones?

You could look at the address of the object, and use that to determine if it is within the range that defines the stack. But the stack may be resized at runtime.

So really, the best answer is that "there's a reason why mark & sweep GC's aren't used with C++". If you want a proper garbage collector, use a different language, one which supports it.

On the other hand, most experienced C++ programmers find that the need for a garbage collector pretty much vanishes when you learn the necessary techniques for resource management (RAII).

查看更多
Evening l夕情丶
6楼-- · 2019-01-22 07:12

The meta question as asked by pax is asked "why would you want to do that" you'll likely get a more informative answer.

Now assuming you're doing this for "a good reason" (perhaps just curiousity) can get this behaviour by overriding operators new and delete, but don't forget to override all 12 variants including:

new, delete, new no throw, delete no throw, new array, delete array, new array no throw, delete array no throw, placement new, placement delete, placement new array, placement delete array.

One thing you can do is put this in a base class and derive from it.

This is kind of a pain, so what different behavior did you want?

查看更多
时光不老,我们不散
7楼-- · 2019-01-22 07:17

A way for MFC classes:

.H

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

.CPP

#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;
}
查看更多
登录 后发表回答