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?
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.
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 :)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With