Destructors
So far we have talked about
using objects
creating objects
But we also need to know how to:
dispose of objects properly
Destructors are used to finalize an object.
The main purpose of a destructor is to clean up after the object, in most cases this means deleting memory.
To find out where to delete, we need to find out who is "responsible" for a certain object. In most cases this will be the creator, but it doesn't have to be.
Assume the following classes:
In this case we can clearly assume that objects of class "Person" are responsible for their pos object (of class Location).
So whenever a Person is deleted we want their Location to be deleted as well.
Defining a destructor:
A destructor is defines like a method that has no return type (just like the constructor), the name tilde-classname (e.g. ~Person) and no parameters. There are no parameterized destructors.
Example:
class Person { ... public: ... ~Person(); ... }
Note on the location of the definition:
Some people define the destructor after all constructors before other methods.
Some people define the destructor after the very last method.
You may do either or, put please again: Do not mix within one project!
Practice!
Example solution
class Car { private: Wheel *wheels[4]; public: Car(); ~Car(); }; // ... Car::Car() { for (int i=0;i<4;i++) wheels[i] = new Wheel(); }
Implementation for destructors:
Just like any other method. Example:
Person::~Person() { delete pos; }
A destructor should
Delete all objects this object was responsible for
Clean up every thing else needed. If the object resembles hardware is should close the connection. (Example on fstreams: deleting an fstream closes the file)
Practice: Implement destructor for car.
Car::~Car() { for (int i=0;i<4;i++) delete wheels[i]; }
How to call destructors:
Destructors are automatically called whenever an object is deleted.
For dynamic object references that means delete ...;
For static object references that means the variable runs out of scope.
Example:
Person *p = new Person("Waldo"); // ... delete p; // <-- calls the destructor
{ Person q("Waldo"); // ... } // <-- call of the destructor
Note for eclipse:
When you create classes in eclipse via the New/Class function it creates a default constructor and a destructor for you. It also follows all naming conventions and provides guards for multiple inclusions.
The destructors in eclipse have the "virtual" keyword. For now you may ignore it. It is important when we talk about inheritance.
Implicit destructors:
If you do not provide a destructor C++ will provide one for you. Unfortunately this one won't do much (unless you're using inheritance).
Here is a complete example:
// Location.h #ifndef LOCATION_H_ #define LOCATION_H_ class Location { private: int x,y; public: Location(); Location(int x,int y) { this->x = x; this->y = y; } ~Location(); int getX() { return x; } int getY() { return y; } }; #endif /*LOCATION_H_*/
// Person.h #ifndef PERSON_H_ #define PERSON_H_ #include <string> using namespace std; class Location; class Person { private: string name; Location *loc; public: Person(string name); Person(string name, Location *l); ~Person(); void cheat(); }; #endif /*PERSON_H_*/
// Location.cpp #include <cstdlib> #include "Location.h" Location::Location() { // Randomizer is initialized in main.cpp x = rand()%100; y = rand()%100; } Location::~Location() { // Nothing to do }
// Person.cpp #include <iostream> #include <string> #include "Person.h" #include "Location.h" Person::Person(string name) { this->name = name; // No location given? Ok, create a new one // Our destructor will make sure it gets deleted. this->loc = new Location(); } Person::Person(string name, Location *l) { this->name = name; // Location given? Use it! // Please note: In this case we decided that Person // is responsible for the location. So it must NOT // be deleted on the caller, but here! this->loc = l; } Person::~Person() { // We're responsible for loc, so let's delete it! delete loc; } void Person::cheat() { cout << name << " is at " << loc->getX() << ", " << loc->getY() << endl; }
// main.cpp #include <cstdlib> #include <ctime> #include <iostream> #include "person.h" #include "location.h" int main() { // Initialize randomizer srand(time(0)); // We don't know anythign about location Person *w = new Person("Waldo"); w->cheat(); delete w; // Creating a location and immediately passing the // responsibility to the Person object Person *m = new Person("Max",new Location(12,34)); m->cheat(); delete m; // Creating a Location Location *fiftyfifty = new Location(50,50); // Passing it (and the responsibility for it) to // Person Person *md = new Person("Middleman",fiftyfifty); md->cheat(); // This also deletes the location delete md; // Typical mistakes! Do not do any of those! // the object reference by fiftyfifty just got deleted! cout << fiftyfifty->getX() << endl; Location *lc = new Location(11,22); Person *p1 = new Person("Person1",lc); Person *p2 = new Person("Person2",lc); // Now both p1 and p2 are responsible for lc. So both // will try to delete it! Bad idea.... // deletes p1 and lc delete p1; // will crash p2->cheat(); return 0; }