Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right usage of destructor

I just started working with C++ and now I have a really basic question.

I wrote 2 classes:

Coordinate:

#include <stdio.h>

class Coordinate {
private:
    int x;
    int y;
public:
    Coordinate(int a, int b) {
        x = a;
        y = b;
    };
    void printTest() {
        printf("%d %d\n", x, y);
    };
};

Test:

class Test {
private:
    int q;
    Coordinate *point;
public:
    Test(int a, int b, int c) {
        q = a;
        point = new Coordinate(b, c);
    };
    virtual ~Test() {
        delete point;
    }
};

main function:

int main() {
    Test *test = new Test(1, 2, 3);
    // ...
    delete test;
    return 0;
}

In my main I worked with an object of the Test class. I wrote my own Test destructor but I am not sure if this destructor work like expected. Does it completly deallocte the memory from test? Or do I have to do something with the q attribute to deallocate it?

like image 419
Blobonat Avatar asked Mar 20 '17 17:03

Blobonat


3 Answers

What you've done is correct, as far as it goes. Valgrind reports

==28151== HEAP SUMMARY:
==28151==     in use at exit: 0 bytes in 0 blocks
==28151==   total heap usage: 3 allocs, 3 frees, 72,736 bytes allocated
==28151== 
==28151== All heap blocks were freed -- no leaks are possible

What you're missing is that the compiler has provided you with a default copy constructor and assignment operator. These will copy the pointer, rather than creating a new pointed-to value, so any time you copy a Test object you will then have two objects whose destructors will both try to delete the same storage. That's a double-free, and it can ruin your day.

To avoid that, C++ programmers use the Rule of Three or Rule of Five when writing classes, or - even better - the Rule of Zero, which says you shouldn't do any new or delete except in a class that exists only to own the storage.

like image 51
Toby Speight Avatar answered Oct 23 '22 15:10

Toby Speight


Yes, this is the correct usage of a C++ destructor (your destructor in Test does not need the virtual keyword as there is no inheritance in your example).

As a rule of thumb every new should be followed by a delete, but when you're using class and instantiation it becomes more subtle than that. Following the Rule of Three or Five, when a class uses dynamic memory you should redefine the class destructor to deallocate accordingly, which you did, so great!

In your program execution, when delete test is invoked it will first deallocate the dynamic memory of point, before deallocating the dynamic memory you set in your main function with your test attribute. In this way you're not leaking memory (yay!) and your memory management has been done accordingly :)

like image 38
Santiago Varela Avatar answered Oct 23 '22 15:10

Santiago Varela


You need a copy constructor to be sure that memory management is doing ok. Because implicitly-generated constructors and assignment operators simply copy all class data members ("shallow copy"). And since you have pointers in your class with allocated data you really need it.

For example if in your part of main code: // ... you do a copy like:

Test testB = *test;

testB has a Coordinate pointer that is pointing to the same memory area that *test. It could cause problems, for example, when testB goes out of scope, it will free the same memory that *test is using.

Copy constructor should look like:

Test(const Test& other)
    : point (new Coordinate(other.x, other.y))
    , q(other.q)
{

}

With this you will be sure that every Coordinate* is initializated ok and released ok.

like image 2
Rama Avatar answered Oct 23 '22 17:10

Rama