Archived

This topic is now archived and is closed to further replies.

aboeing

Am I on the heap? & Virtual Functions

Recommended Posts

Hi, I am trying to write a class to let me know if the object has been constructed on the heap. The following code works:
class HeapTest {
public:
	HeapTest() {
		printf("0x%x HeapTest alive!\n",this);
	}
	~HeapTest() {
		printf("0x%x goodbye from HeapTest!\n",this);
	}
	static void *operator new (size_t size) {
		void *memPtr=::operator new(size);
		printf("0x%x heap!\n",memPtr);
		return memPtr;
	}
	static void *operator new[] (size_t size) {
		void *memPtr=::operator new(size);
		printf("0x%x heap array!\n",memPtr);
		printf("sizeof(me):%d\n",sizeof(HeapTest));
		printf("therefore, i am creating %d of me\n",(size/sizeof(HeapTest))-1);
		printf("therefore the following are on the heap:\n");
		char *htp=(char *) memPtr;
		for (unsigned int i=1;i<size/sizeof(HeapTest);i++)
			printf("0x%x (%d)\n",htp+i*sizeof(HeapTest),i);
		return memPtr;
	}
	void SayHi(char *msg) {
		printf("0x%x hi:%s\n",this,msg);
	}
	int x;	

};

int main() {

	HeapTest ht;
	ht.SayHi("ht");

	HeapTest *htp = new HeapTest;
	htp->SayHi("htp");
	delete htp;

	HeapTest *htpa = new HeapTest[3];
	htpa[2].SayHi("htpa[2]");

	delete [] htpa;
	printf("eop\n");
}
Now the thing is, I wanted to use this as a base class so that I could let my memory manager correctly delete things. But when I add virtual functions everything goes wrong. Any solutions? Thanks.

Share this post


Link to post
Share on other sites
Heres the program output btw:

0x12ff70 HeapTest alive!
0x12ff70 hi:ht
0x431b70 heap!
0x431b70 HeapTest alive!
0x431b70 hi:htp
0x431b70 goodbye from HeapTest!
0x431b60 heap array!
sizeof(me):4
therefore, i am creating 3 of me
therefore the following are on the heap:
0x431b64 (1)
0x431b68 (2)
0x431b6c (3)
0x431b64 HeapTest alive!
0x431b68 HeapTest alive!
0x431b6c HeapTest alive!
0x431b6c hi:htpa[2]
0x431b6c goodbye from HeapTest!
0x431b68 goodbye from HeapTest!
0x431b64 goodbye from HeapTest!
eop
0x12ff70 goodbye from HeapTest!

Share this post


Link to post
Share on other sites
quote:
Hi, I am trying to write a class to let me know if the object has been constructed on the heap.

Then make the constructor private and construct objects through a factory that returns objects that have been newed into existance.

Share this post


Link to post
Share on other sites
Hi,
Sorry, I should have mentioned that I didnt want to do that. I still want to be able to create variables on the stack, but I want to know if it is deleteable.
Private constructors&destructors is too restrictive.
Thanks,
-Adrian

Share this post


Link to post
Share on other sites
Hi, I am trying to write a class to let me know if the object has been constructed on the heap.

There exist no foolproof method to do this.
See Scott Meyers, More Effective C++, Item #27 for a discussion.

Note - your base class destructor must be virtual.


[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]


[edited by - Fruny on October 13, 2003 10:28:28 PM]

Share this post


Link to post
Share on other sites
There exist no foolproof method to do this.

Well I think I''ve figured it out. Other than not working for multiple threads, but thats nothing that a semaphore cant solve.

Let me know if you can spot any issues with this:


static int nHeap; //this will let me know if I am on the heap


class HeapTest {
public:
HeapTest() {
printf("0x%x HeapTest alive.\n",this);
if (nHeap>0)
printf("0x%x And I am on the Heap!\n",this);
nHeap--;
}
virtual ~HeapTest() {
printf("0x%x goodbye from HeapTest.\n",this);
}
static void *operator new (size_t size) {
void *memPtr=::operator new(size);
nHeap=1;
return memPtr;
}
static void *operator new[] (size_t size) {
void *memPtr=::operator new(size);
nHeap=(size/sizeof(HeapTest))-1;
return memPtr;
}
virtual void SayHi(char *msg) {
printf("0x%x hi:%s\n",this,msg);
}
int x;
};


Output from a test program:

0x12ff6c HeapTest alive.
0x12ff6c hi:ht
0x431b60 HeapTest alive.
0x431b60 And I am on the Heap!
0x431b60 helo from ifht
0x431b60 IFHT:htp
0x431b60 goodbye from InheritFromHeapTest.
0x431b60 goodbye from HeapTest.
0x431b54 HeapTest alive.
0x431b54 And I am on the Heap!
0x431b5c HeapTest alive.
0x431b5c And I am on the Heap!
0x431b64 HeapTest alive.
0x431b64 hi:htpa[2]
0x12ff58 HeapTest alive.
0x12ff58 hi:stackht:I am on the stack
0x431b64 goodbye from HeapTest.
0x431b5c goodbye from HeapTest.
0x431b54 goodbye from HeapTest.
eop
0x12ff58 goodbye from HeapTest.
0x12ff6c goodbye from HeapTest.

Seems to work just fine...

See Scott Meyers, More Effective C++, Item #27 for a discussion.
Dont have the book, a friend told me he used static vector of addresses. But then he wouldnt be able to support the new[] operator..(?) Plus his solution would have more overhead..

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Well for a start, a very common feature of memory management is to allocate memory without constructing an object on it, and your class would fail completely under these circumstances


my_class* pmy_memory_pool = my_class.operator new(100*sizeof(my_class));


Your code now expects one object to be constructed on the heap
however i could use placement new to construct 100 objects
on the heap, 99 of which would be thought to be on the stack by your code. Also after allocating this memory i could create the next object on the stack, and not create anything on the free-store..

my_class instance_of_my_class_on_the_stack; //o_0

Your code is expecting the constructor for a heap based object, but its getting one on the stack, now when you try to delete the memory occupied by instance_of_my_class_on_the_stack, you will get your undefined behaviour. The same problem exists with operator []. It all comes from the fact that a call to operator new doesnt mean that a constructor will be called after it, but your code relies on just this, and so wont work under anything but the simplest of circumstances

Share this post


Link to post
Share on other sites
Hey thanks heaps (haha!) for the feedback, I hadn''t thought of that at all!
**SNIP** Infact, I just realised that my code wasn''t working at all. If you call operator new [] with an inherited class, it will have a different class size, **SNIP**

*time passes*
Actually, I have now got, what I believe to be a fully working solution:

#include <vector>
using namespace std;

class HeapTest {
public:
HeapTest() {
printf("0x%x HeapTest alive.\n",this);
int i;
for (i=0;i<low_address.size();i++)
if (this>=low_address[i]) {
//printf("0x%x is greater than 0x%x\n",this,low_address[i]);

if (this<high_address[i]) {
printf("0x%x I am on the heap!\n",this);
}
}

}
virtual ~HeapTest() {
printf("0x%x goodbye from HeapTest.\n",this);
}
static void *operator new (size_t size) {
void *memPtr=::operator new(size);

low_address.push_back(memPtr);

char *bytePtr = (char *)memPtr;
bytePtr+=size;
high_address.push_back((void *)bytePtr);

return memPtr;
}
static void *operator new[] (size_t size) {
void *memPtr=::operator new(size);

low_address.push_back(memPtr);

char *bytePtr = (char *)memPtr;
bytePtr+=size;
high_address.push_back((void *)bytePtr);

return memPtr;
}
virtual void SayHi(char *msg) {
printf("0x%x hi:%s\n",this,msg);
}
int x;
static vector <void *> low_address;
static vector <void *> high_address;
};

vector <void *> HeapTest::low_address;
vector <void *> HeapTest::high_address;


By using TWO vectors to store the high and low addresses of the memory we have allocated then we can determine if we are on the heap by searching through the vectors and ensureing we are inbetween the two addresses.

I dont think there are any flaws in this? Now my solution is slower than Scott Meyers

Again I would love to get feedback on this design.

Share this post


Link to post
Share on other sites
Hmm....

Would it help if you make HeapTest a templated class? If you made it HeapTest<class T> (perhaps inherited from a BaseHeapTest so that you can still aggregate them all), and then used it as 'class DerivedClass : public HeapTest<DerivedClass>,' you'd be able to cast 'this' to type T* (and thus get the correct base address of the object).



Superpig
- saving pigs from untimely fates, and when he's not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4
ry. .ibu cy. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu

[edited by - Superpig on October 14, 2003 1:35:35 PM]

[edited by - Superpig on October 14, 2003 1:36:04 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Your solution is better than the last one, and i cant see how the idea wouldnt work in most cases.

You would get problems if there isnt enough room in the free-store to push_back the high and low addresses, in which case your checks would fail, and so would your system, this isnt much a problem on computers with large memory, but it could be on machines where memory is very limited.

Theres more ideas on complete solutions to the problem here
http://www.aristeia.com/BookErrata/M27Comments_frames.html

Share this post


Link to post
Share on other sites
The solutions from that webpage all seem to involve some magic-number dodgyness, and I dont think they would pass the
my_class* pmy_memory_pool = my_class.operator new(100*sizeof(my_class));
test.
One thing I did realise from the webpage, is that this solution is FAR slower than Scott's because you cant even remove address's from the vector as they get used (your vector would get very large -> slow search time)...
Perhaps you could check if the address of this in the constructor was at the begining address of one of the memory blocks and slowly remove portions of the space between the vectors, but again you would want to know the size of the actual classes before doing this.

superpig's idea might solve this problem. infact it does. But you still cant use my second (counter) solution, as you still cant deal with:
my_class* pmy_memory_pool = my_class.operator new(100*sizeof(my_class));
my_class* pmy_memory_pool = my_class.operator new(100*sizeof(my_class));

So, how about this now: ?

#include <vector>
using namespace std;

vector <void *> g_low_address;
vector <void *> g_high_address;

#define sizeof_HeapTest 4

template<class T> class HeapTest {
public:
HeapTest() {
printf("0x%x HeapTest alive.\n",this);
for (int i=0;i<g_low_address.size();i++)
if (this>=g_low_address[i]) {
//printf("0x%x is greater than 0x%x\n",this,low_address[i]);

if (this<g_high_address[i]) {
printf("0x%x I am on the heap!, my size is: %d [l:0x%x h:0x%x]\n",this,sizeof(T),g_low_address[i],g_high_address[i]);
printf("THIS DOESNT WORK! sizeof(HeapTest):%d\n",sizeof(HeapTest));
printf("we need a #define sizeof(HeapTest:not-functions):%d\n",sizeof_HeapTest);
char *bytePtr = (char *)g_low_address[i];
bytePtr+=sizeof_HeapTest;
if (this==(void *)bytePtr) {
printf("I can move the low address\n");
g_low_address[i]=(void*)(bytePtr+sizeof(T)-sizeof_HeapTest);
printf("Now: [l:0x%x h:0x%x]\n",g_low_address[i],g_high_address[i]);
if ((void*)(bytePtr+sizeof(T)) == g_high_address[i]) {
printf("And now I can remove these entries all together! Hurrah!\n");
g_low_address.erase(g_low_address.begin() + i);
g_high_address.erase(g_high_address.begin() + i);
}
}
goto exit_the_loop;
}

}
exit_the_loop:
;
}
virtual ~HeapTest() {
printf("0x%x goodbye from HeapTest.\n",this);
}
static void *operator new (size_t size) {
void *memPtr=::operator new(size);

// printf("s(1)%d a(?)%d\n",sizeof(T),size);

g_low_address.push_back(memPtr);

char *bytePtr = (char *)memPtr;
bytePtr+=size;
g_high_address.push_back((void *)bytePtr);


return memPtr;
}
static void *operator new[] (size_t size) {
void *memPtr=::operator new(size);

// printf("s(1)%d a(?)%d\n",sizeof(T),size);

g_low_address.push_back(memPtr);

char *bytePtr = (char *)memPtr;
bytePtr+=size;
g_high_address.push_back((void *)bytePtr);


return memPtr;
}
virtual void SayHi(char *msg) {
printf("0x%x hi:%s\n",this,msg);
}
int x;
};

program output:
0x4315a4 I am on the heap!, my size is: 268 [l:0x4315a0 h:0x4317bc]
THIS DOESNT WORK! sizeof(HeapTest):8
we need a #define sizeof(HeapTest:not-functions):4
I can move the low address
Now: [l:0x4316ac h:0x4317bc]
0x4315a4 helo from ifht
0x4316b0 HeapTest alive.
0x4316b0 I am on the heap!, my size is: 268 [l:0x4316ac h:0x4317bc]
THIS DOESNT WORK! sizeof(HeapTest):8
we need a #define sizeof(HeapTest:not-functions):4
I can move the low address
Now: [l:0x4317b8 h:0x4317bc]
And now I can remove these entries all together! Hurrah!

Now I can clean up after myself (thanks superpig! - [actually i was hopeing you would read this form, so you could use this for your mempointer problems ]), the other point is whether to use a list now or a vector.

edit:
I can think of two limitations:
a) memory must be assigned in a continues fashion for each new call. (this is gauranteed though isn't it?)
b) the objects in an array must be constructed consecutively from a low address to a high address in order for the clean up to work. regardless - the actual am I on the heap? question, should be answered.
any others?
edit#2:
actually inheriting something that was inherited from the baseclass doesnt work either - still this is just case (b) again really
solutions anyone?
[edited by - aboeing on October 14, 2003 12:28:37 AM]

[edited by - aboeing on October 15, 2003 1:02:06 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Why cant you remove the addresses from the vector?

One possibility would be to remove it when operator delete is called.


static void operator delete(void* pmemory, std::size_t size)
{
try{
if(pmemory==0)return;
std::vector<void*>::iterator iter= g_low_address.begin();
std::vector<void*>::iterator iter_high =g_high_address.begin();
for( ; iter!=g_low_address.end(); ++iter , ++iter_high)
{
if (this==*iter)
{
::operator delete(pmemory);
g_low_address.erase(iter);//delete low memory element
g_high_address.erase(iter_high);//delete high memory element
break;
}
}
}//try block
catch(std::exception& exp)
{
std::cout<<exp.what()<<std::endl;
}
}//operaotr delete


Havnt checked this by compilation. But something along these lines should work, operator delete would have to be called on the first element of memory at some time during execution, otherwise you would get memory leaked. It would also be resonable to assume that a call to release memory that isnt at the start of an allocated block would be an error, and so theres no need to deallocate it(which is why ive done the ::operator delete inside the if statement).

Something similar could be done with operator[] delete too.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
if (pmemory==*iter) would be better than if(this==*iter)

Share this post


Link to post
Share on other sites
Good idea, but it wont work in that form, because what if the memory is allocated as a pool, and then only a few of them are deleted.. im not sure how common that situation is?
But, i guess your pritty much gauranteed that everything is constructed by the time you delete something?? I dont know enough about how memory managers work to know if that will work.

If it was changed so that it just removed the range of addresses that were deleted then that would work? Right?

And again, is it likely that the memory manager will delete objects in a non-consecutive order? (otherwise ill have to allow ''splitting'' the address ranges.. eeevil..)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Well when you use a memory pool, and you destroy an item in it, you dont release the memory it occupied, you just call the destructor for the object in it, so the memory which was allocated is still available. As far as i know the only way to release the memory in the memory pool is to call operator delete(assuming it was created with operator new) on a pointer to the first address of the allocated block, anything else is undefined as far as i know.

This is because a common implementation is to put a small amount of information at the start of the memory block which describes the size of the allocated memory block, operator delete uses this to know how much memory to deallocate. If a user tryed to use operator delete on an area of memory which didnt contain this information, then operator delete would probably throw an exception(just guessing here, correct me if im wrong). So unless the user is trying to use delete on things which shouldnt be deleted, the code would work fine.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by Anonymous Poster
then operator delete would probably throw an exception(just guessing here, correct me if im wrong).


Scratch that, operator delete is guaranteed not to throw an exception

Share this post


Link to post
Share on other sites
OK, I might well be missing some major issues here, but how about:

class Stack{
public:
static int topOfStack;
static bool isOnStack(int address);
};

int Stack::topOfStack = -1;
bool Stack::isOnStack(int address){
char stackMem;
int bottomOfStack = reinterpret_cast<int>(&stackMem);
return (address > bottomOfStack && address < topOfStack);
}

class Object{
public:
Object(){
if (Stack::isOnStack(this)){
std::cout << "Object on stack\n";
}
else {
std::cout << "Object on heap\n";
}
}
};

int mainFunc(){
Object o1;
Object* o2 = new Object();
}

int main(int, char**){
char stackMem;
Stack::topOfStack = reinterpret_cast<int>(&stackMem);
return mainFunc(); // just to ensure a change in stack position

}


Of course, this won't detect objects in the data segment - don't know if there's a solution to this.

On another topic:
>> sizeof(me):4
>> therefore, i am creating 3 of me

Huh? if sizeof(me) is 4 shouldn't you be creating 4 of 'me'?

Enigma

EDIT: probably can't assume that the first char in main will be created on the heap before any other stack objects in main, so moved everything else into another function.

[edited by - Enigma on October 15, 2003 1:41:49 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Just because something isnt on the stack, doesnt mean it is on the heap, global variables arnt on the heap or the stack, neither are local static variables.

Share this post


Link to post
Share on other sites
quote:
Original post by Fruny
There exist no foolproof method to do this.
See Scott Meyers, More Effective C++, Item #27 for a discussion.


Please, read the thing. He talks extensively about these issues. If there was some way to do it even un-portably you might have seen it done before with completely different implementations for each platform.

Share this post


Link to post
Share on other sites
Well when you use a memory pool, and you destroy an item in it, you dont release the memory it occupied, you just call the destructor for the object in it, so the memory which was allocated is still available.
Oh, well in that case my addition of trying to remove entries from the vector in the constructor wouldnt work.

In which case
One possibility would be to remove it when operator delete is called.
Is probably the best idea. Unfortunately this means your going to have a very large vector of addresses to search through.

Which brings me to a second, unrelated question, whats the best data structure for storing this big "list" of addresses? A tree, a list, stick to a unsorted vector? Whats a good (fast) library that implements these? (Or am I going to have to dig up the old algorithms&data structures book...)

re:Enigma: also, I cant find the thread right now, but I think S1CA pointed out how windows can play around and be evil with the stack&heap addressses so that they arn't always in that order, and futhermore I dont think all OS's have stack < heap, some do the opposite.

Huh? if sizeof(me) is 4 shouldn't you be creating 4 of 'me'?
No, check out the function again, the size of the requested mem is divided by the sizeof(me) [this doesn't really matter anyway, this code doesnt work =P]

Edit:
Had to do some wierdo formating to make it display correctly..


[edited by - aboeing on October 16, 2003 10:38:59 AM]

Share this post


Link to post
Share on other sites
Why can''t you just combine my HeapTest<> template idea with a list of pointers to this?

The operator new would add a pointer to the newly-allocated block to the list, and then the HeapTest<> constructor would convert the this pointer to type T* (the whole get-the-actual-this-pointer thing we discussed before) and search for it in the list. If it finds it, it sets a flag within the object (bIsOnHeap) and removes it from the list (that way, the list should never get too large).

Not sure about arrays. In Enginuity, as I think you''ve read, I have seperate array objects (CMMBlob/CMMDynamicBlob).

As far as cheating goes - CMyClass *ptr=(CMyClass*)malloc(sizeof(CMyClass)); - well, at some point you''ve just got to let it break. If users are going to mess around like that, they get all that''s coming to them.

Superpig
- saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4
ry. .ibu cy. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu

Share this post


Link to post
Share on other sites
Yeah I tried that, (didnt I? in the code posted with the template stuff..) the problem was with inheritance, if we have

class IHeapTest:public HeapTest<IHeapTest> {
int z;
};
//yaay we know my size! everything is peachy.


class IIHeapTest: public IHeapTest {
char name[256];
};
//ohno! what now?


This might just be my lack of knowledge on templates comming in..

In fact you dont even need to use templates if you dont need to worry about arrays, in fact, you woudln''t even need to store the high_address values. I think this is what Scott Meyers implementation did.

Not sure about arrays. In Enginuity, as I think you''ve read, I have seperate array objects (CMMBlob/CMMDynamicBlob).
Yeah, maybe its just not (nicely)doable without seperate array objects. (side note: Enginuity was great! Really p@#$ed me off thou, because I just spent weeks implementing that stuff and then you released an article which takes an hour to cover everything.. )
Another thing I had thought of was to have a seperate method for each actually removing a section of memory, and then every class''s constructor could call that method with sizeof(myself).
But as was pointed out (Well when you use a memory pool, and you destroy an item in it, you dont release the memory it occupied, you just call the destructor for the object in it, so the memory which was allocated is still available ) this would fail for this system. Actually, so would the template method??

As far as cheating goes - CMyClass *ptr=(CMyClass*)malloc(sizeof(CMyClass));
I hadn''t thought of that. Im definately not touching that one. Mind you if you could gaurantee the os''s stack&heap behaviour you could pick that one up using Enigma''s method. (globals&statics would be easy enough to add to that method)

Share this post


Link to post
Share on other sites
The correct answer was given long ago ("don''t do this, it''s wrong and it won''t work, use a factory and forfeit stack objects"). Why have you ignored it?

Share this post


Link to post
Share on other sites