Ryan Brown
Ryan Brown

Reputation: 1085

Detecting stack or heap allocation

I have a class I’d like to be able to set a flag in that says if it is heap allocated so it can properly clean up after itself and not try to delete itself if it’s on the stack. The problem is...I can’t seem to override both new and the constructors at the same time. So it goes from my new overload that sets the isHeapAllocated flag and then into my constructor which resets the flag.

void* String8::operator new(size_t size)
{
    String8* string = (String8*)malloc(size);
    if(string == null)
        Exception("allocation fail : no free memory");
    string->isHeapAllocated = true;
    return string;
}

String8::String8() 
{
    isHeapAllocated = false;
}

So new String8() sets the isHeapAllocated flag and then resets it to false. Is there any way to do this?

Upvotes: 4

Views: 2027

Answers (4)

Emilio Garavaglia
Emilio Garavaglia

Reputation: 20730

It will not work as intended:

The new operator return unitialized memory to be given to the constructor. You -correctly- do String8* string = (String8*)malloc(size);, but *string, at this stage is not yet a String8 object: it is just the memory bulk that will contain it.

So string->isHeapAllocated = true; in fact sets a flag inside a not yet constructed object (that's UB).

Admitting this will not compromise the OS process, so that the program will not crash (you write memory that belongs already to you, after all ...), when you will later do something like String8* ptr = new String8;, after new returns, the String8::String8 constructor is called, and the member will be set back to "false" independently on what you did in the new operator overload.

The idiomatic way to manage C++ objects is let who allocate to be responsible to deallocate. (and if "who" it is the stack, it just do that by definition).

Upvotes: 3

lapk
lapk

Reputation: 3918

Not sure why you need this, really. It's caller's responsibility to call delete if needed and your class's destructor should not be different whether it's called on the object on stack or on the heap... But, maybe, you are doing some special purpose class... Here is my quick take on it.

EDIT: You should also, probably, add custom delete operator to your class, unless you know that global delete calls a deallocation function that matches the allocation function you use in your custom new operator.

#include <cstdlib>
#include <iostream>

namespace so
{

class _test_
{
 private:
  static bool flag_allocation_heap;
  bool flag_heap;

 public:
  _test_()
      : flag_heap( flag_allocation_heap )
  {
   flag_allocation_heap = 0;
   std::cout << flag_heap << std::endl;
  }

  void * operator new( std::size_t _size )
  {
   _test_ * test_ = static_cast< _test_ * >( std::malloc( _size ) );
   flag_allocation_heap = 1;
   return ( test_ );
  }
};

bool _test_::flag_allocation_heap = 0;

} // namespace so

int main()
{

 so::_test_ test_stack_;
 so::_test_ * test_memory_ = new so::_test_;

 delete test_memory_;

 return( 0 );
}

Output:

0
1

Upvotes: 0

Jon Purdy
Jon Purdy

Reputation: 54981

This is a bad idea, but here’s a way to do it that doesn’t invoke undefined behaviour.

#include <iostream>
#include <memory>
#include <set>

using namespace std;

class C {
public:

  void* operator new(size_t size) {
    C* c = static_cast<C*>(::operator new(size));
    heap_instances.insert(c);
    return c;
  }

  C() : heap_allocated(heap_instances.find(this) != heap_instances.end()) {}

  const bool heap_allocated;

private:
  static set<const C*> heap_instances;
};

set<const C*> C::heap_instances;

int main(int argc, char** argv) {
  cout << boolalpha;

  C stack;
  cout << stack.heap_allocated << '\n'; // false

  C* heap_nozero = new C;
  cout << heap_nozero->heap_allocated << '\n'; // true
  delete heap_nozero;

  C* heap_zero = new C();
  cout << heap_zero->heap_allocated << '\n'; // true
  delete heap_zero;
}

You can remove pointers from heap_instances when you’re done with them, of course, and use a more suitable container if you’re running in a multithreaded environment. But again, I wouldn’t recommend that you actually do this—deciding behaviour based on allocation is not something an object ought to do.

The only legitimate reason I can think of for this is to enable delete this. While that’s safe if you’re careful not to access members after the object’s suicide, it’s usually saner to let objects manage the lifetimes of other objects.

Upvotes: 1

Quonux
Quonux

Reputation: 2991

Note that the construtor gets called if it is allocated on the stack or the heap and there is no way for the object to detect if it was allocated on the stack or in the heap.

To create an object at the stack you don't use any memory allocation functions like this

String8 myString;

To create it on the heap you do

String8 *myString = new String8();

note that you do have to do the cleanup manually after not using the object anymore.

For the use of Heap objects bound to stack scope you can check out the RAII principle which is used intensly by c++ programs (see here for a better explaination of the difference of heap allocation and stack allocation).

Upvotes: 0

Related Questions