Search code examples
c++memory-management

Check if memory is on the Heap?


I have a class whose constructor takes the address of an object as a parameter.

MyClass(OtherClass * otherClass);

In the Destructor of this class I try to delete the instance of OtherClass.

~MyClass() {
    if(otherClass != nullptr) {
        delete otherClass;
    }
}

The issue I'm having is when I call this constructor I call it with an element from the stack instead of from the heap, thus I call it as below:

MyClass myClass(&otherObject);

so when the myClass object goes out of scope I get an exception. How can I find out if my OtherObject variable was declared on the stack or the heap? Or in other words, how can I know if I can delete the object?


Solution

  • While there are system specific approaches which will probably be able to tell whether memory is from heap or from a stack, that actually doesn't really help: you may have got a pointer to a member of another object on the heap. The memory would be on the heap but you are still not responsible for deleting the object. Put differently: do not go down the path you are on!

    The proper way to deal with the problem is to make the ownership semantics blatantly clear in the interface and go with that. There are essentially two directions you can take:

    1. Your class can deliberately not take over responsibility for the pointer passed in the constructor! Instead, it is the responsibility of the user of your class to deal with these objects and to guarantee that they are valid while your objects exists. If you can get pointers to stack (or member) objects the user already has to guarantee the validity of these objects anyway and for other ways it may be entirely trivial for the user to deal with them, e.g., by managing them via a std::unique_ptr<OtherClass>.
    2. Your class takes responsibility of all objects passed and it will delete all of them. It becomes the responsibility of the caller to not pass pointers to objects managed elsewhere, e.g., to objects on the stack or member objects.

    There is sort of a hybrid approach where your class takes responsibility of objects some times but not always. However, the implementations of such an approach is really a combination of the two approaches above: you'd take a suitable smart pointer as constructor argument and it is the user's responsibility to make sure that the smart pointer is constructed appropriately by the user of your class. For example, your class could take a std::shared_ptr<OtherClass> for which the normal construction will delete the object. When the user wants to pass in a pointer to an otherwise owned object the std::shared_ptr<OtherClass> would be constructed with a deleter which does not delete the pointer. Here is a simple program demonstrating the two different management strategies for std::shared_ptr:

    #include <iostream>
    #include <memory>
    
    struct foo {
        char const* name;
        foo(char const* name)
            : name(name) {
            std::cout << "foo::foo(" << name << "): " << this << "\n";
        }
        ~foo() {
            std::cout << "foo::~foo(" << name << "): " << this << "\n";
        }
    };
    
    int main() {
        std::shared_ptr<foo>(new foo("heap"));
        foo f("stack");
        std::shared_ptr<foo>(&f, [](auto){});
    }