Saksham
Saksham

Reputation: 9380

Does a destructor automatically deallocates heap memory for member variables?

I have a few doubts related to destructor.

class cls
{
    char *ch;
public:
    cls(const char* _ch)
    {
        cout<<"\nconstructor called";
        ch = new char[strlen(_ch)];
        strcpy(ch,_ch);
    }
    ~cls()
    {
        //will this destructor automatically delete char array ch on heap?
            //delete[] ch; including this is throwing heap corruption error
    }
    void operator delete(void* ptr)
    {
        cout<<"\noperator delete called";
        free(ptr);
    }
};
int main()
{
    cls* cs = new cls("hello!");
    delete(cs);
    getchar();
}

Also, as a destructor is automatically called upon delete why do we need an explicit delete when all the logic can be written in destructor?

I am super confused regarding operator delete and destructor and couldn't make out their specific usage. An elaborate description would be very helpful.

EDIT: My understanding based on the answers: For this particular case, a default destructor will corrupt the char pointer so we need to explicitly delete the char array first other wise it will result in memory leak. Please correct me if I am wrong.

Upvotes: 5

Views: 14463

Answers (7)

Manu343726
Manu343726

Reputation: 14174

C++ memory magnament is based on RAII. That means that destructors are called when the lifetime of a variable ends.

For example:

class Foo
{
public:
   Foo() { cout << "Constructor!!!" << endl; }
   ~ Foo() { cout << "Destructor!!!" << endl; }
};

int main()
{
   Foo my_foo_instance;
}

Prints:

Constructor!!!
Destructor!!!

Because the constructor is called in the initiallization of my_foo_instance (At the declaration), and the destructor is called when the lifetime of my_foo_instanceends (That is, at the end of main() ).

Also, this rules works for any context, including class attributes:

class Foo1
{
public:
   Foo1() { cout << "Foo1 constructor!!!" << endl; }
   ~ Foo1() { cout << "Foo1 destructor!!!" << endl; }
};

class Foo2
{
private:
    Foo1 foo1_attribute;
public:
   Foo2() { cout << "Foo2 constructor!!!" << endl; }
   ~ Foo2() { cout << "Foo2 destructor!!!" << endl; }
};

int main()
{
   Foo2 my_foo2_instance;
}

Prints:

Foo1 constructor!!!
Foo2 constructor!!!
Foo2 destructor!!!
Foo1 destructor!!!

The trace of the program is:

  • Main starts
  • Initiallization of my_foo2_instance: Call to Foo2 constructor
  • First of all, Foo2 initializes its attributes: Call to Foo1 constructor
  • Foo1 has no attributes, so Foo1 executes its constructor body: cout << "Foo1 constructor" << endl;
  • After attributes initiallization, Foo2 executes its constructor body: cout << "Foo2 constructor" << endl;
  • End of main scope, so end of my_foo2_instance lifetime: Call to Foo2 destructor
  • Foo2 destructor executes its body: cout << "Foo2 destructor" << endl;
  • After the destructor, the lifetime of the attributes of Foo2 ends. So: Call to Foo1 destructor
  • Foo1 destructor executes its body: cout << "Foo1 destructor" << endl;
  • After the destructor, the lifetime of the attributes of Foo1 ends. But Foo1 has no attributes.

But what you forget is that a pointer its a basic type, so it has no destructor. To destroy the object pointed by the pointer (Thats is, finallize the life of the pointee object), use use delete operator in destructor body.

Upvotes: 2

David G
David G

Reputation: 96790

delete is not a function (though you can overload it); and yes, it is good practice that the logic for the deallocation be written in the destructor. But it is incorrect to assume that the deallocation will be automatically performed by the destructor. The thing is that the destructor will be called at the end of the object's lifetime, but what it does it dependent upon the code you write for it. That is, you should call delete[] on ch inside the destructor:

~cls()
{
    delete[] ch;
    ch = nullptr;
}

Moreover, I believe the heap corruption error is coming from that fact that you did not leave enough room in the initialization of ch for the null byte \0. You should also be using the the member-initializer list. Change your constructor to this:

cls(const char* _ch) : ch(new char[1+strlen(_ch)])
{
    std::cout << "\nconstructor called";
    std::strcpy(ch, _ch);
}

There are many improvements that can be made to your code. Namely, using std::string and following the Rule Of Three. Your code also doesn't need a operator delete() overload. cs should be stack allocated:

#include <iostream>
#include <string>

class cls
{
    std::string ch;
    public:
        cls() { std::cout << "default constructor called\n"; }

        cls(std::string _ch) : ch(_ch)
        {
            std::cout << "constructor called\n";
        }

        cls(cls const& other) : ch(other.ch)
        {
            std::cout << "copy-constructor called\n";
        }

        ~cls() { std::cout << "destructor called\n"; }
};

int main()
{
    cls cs("hello!");

    std::cin.get();

} // <-- destructor gets called automatically for cs

Upvotes: 5

ondrejdee
ondrejdee

Reputation: 466

My take:

1) The short answer is no.

2) As for "why not", consider the following example:

cls create()
{
   cls Foo("hello"); // This allocates storage for "ch"
   return Foo; 
} // Return variable is by value, so Foo is shollow-copied (pointer "ch" is copied).
  // Foo goes out of scope at end of function, so it is destroyed. 
  // Do you want member variable "ch" of Foo to be deallocated? Certainly not! 
  // Because this would affect your returned instance as well!

Recommendations:

If you want to see whether your code leaks storage, you may use an excellent tool valgrind, http://valgrind.org/

As for what to read to understand this topic better, I would recommend the standard C++ literature, plus have a look at smart pointers, e.g. unique pointer http://www.cplusplus.com/reference/memory/unique_ptr/ which will help you understand the topic and everything will be put to place.

Upvotes: 0

AnT stands with Russia
AnT stands with Russia

Reputation: 320381

Destructor never deallocates anything on its own accord. Destructor will only implicitly call destructors for class subobjects and execute whatever code you put into the destructor's body. Since in your case the subobject ch is of raw pointer type, it has no destructor. So nothing will be done in your case. Since it is you who allocated the memory, it is you who's responsible for deallocating it. In short, yes, you do need that delete[] ch in your destructor.

If you want that memory deallocated automatically, use a smart pointer class instead of a raw pointer. In that case the destructor of your class will automatically call the destructor of the smart pointer subobject, which will deallocate memory for you. In your specific example an even better idea would be to use std::string to store the string inside the class object.

The heap corription in your case is caused by the fact that you allocate insufficient memory for your string, which leads to an out-of-bounds write in strcpy. It should be

ch = new char[strlen(_ch) + 1];
strcpy(ch,_ch);

The extra space is needed for the terminating zero character.

Upvotes: 1

nullptr
nullptr

Reputation: 11058

Well, a default destructor deallocates memory that is used by member variables (i.e. the member pointer ch itself ceases to exist), but it does not automatically deallocate any memory that is referenced by member pointers. So there is a memory leak in your example.

Upvotes: 7

Nik Bougalis
Nik Bougalis

Reputation: 10613

No, the destructor won't magically delete the memory pointed to by ch for you. If you called new (you did in the constructor) then you must also call delete at some appropriate time.

A destructor executes when an object is being destroyed. This can be when an automatic object (that is something allocated on the stack) is about to go out of scope, or when you explicitly delete an object allocated with new.

Generally, think of new as a way of getting memory allocated, a constructor as a way of taking that memory and making it into an object, a destructor as taking an object and destroying it, leaving behind a chunk of memory and delete as taking that chunk of memory and deallocating it.

As a convenience for you, when you call new the compiler will call the constructor for you after it allocates the memory you requested and when you call delete the compiler will automatically invoke the destructor for you.

You are getting heap corruption errors because you have a buffer oveflow: you are not allocating space for the null terminating byte that strcpy appends.

Remember a C string is a sequence of bytes followed by a null byte. This means that a string of length 5 actually requires 6 bytes to store.

Also remember that you can and should use std::string instead of C style arrays to save yourself the trouble and avoid having to write error-prone code when there's a very robust and full-featured implementation already available for your use.

With the notable exception of homework/learning exercises, there is hardly a situation where you should implement C style strings directly instead of using std::string.

The same (albeit a little less strict) goes for dynamic arrays in general. Use std::vector instead.

Upvotes: 4

junix
junix

Reputation: 3211

There is no use in override the delete Operator for a specific class. That's what the global delete Operator is for.

What you should do is a delete[] on ch in the destructor. This has to be done explicitly, as the delete Operator only deallocates the memory directly allocated to store the class' instance. As you are allocating further Memory in the constructor you have to free it upon destruction.

As a rule of thumb you can assume that con- and destructor need to be coded symmetricly. For every new in the constructor, has to be a delete in de destructor.

Oh and by the way: You must not mix C++ allocators (new/delete) with C allocators (malloc/free). What you allocate in C++, you have to free in C++ and vice versa.

Upvotes: 1

Related Questions