If your language is somewhat of a fully object oriented programming language then it probably has some form of constructors and destructors. But what good are they? How do you use them to your full advantage to keep your objects “valid”? What is this whole idea of a default constructor and why in the heck do I want to create my own constructor if the default constructor is there for me automatically? We address some of these issues in todays entry on Martyr2’s Programming Underground!
Constructors and destructors have been around for a long long time. C++ uses them and be it that C++ uses it, you can bet that Java, C# and a host of others are going to use them too. They have become a necessity in the world of OOP (object oriented programming). Constructors are methods of classes which contain the same name as the class and used to throw the object into what is known as “state”. Throw into state? What the heck does that mean Martyr? Talk some damn sense man!
Throwing an object into state means that upon its creation it is put into a valid form or “state”. Just like matter has various states, so too can objects… in a more simplistic way I suppose. Lets take for instance a “ball” object. A ball is round, it has perhaps a color, a texture, and some actions. But when we create the object we have to tell it a bit about what it means to be an instance of a ball. If we want a red ball which is smooth, we have to tell it that when we create it. Otherwise the ball will assume it is just colorless and has no texture.
Now in most languages a constructor is provided for your object automatically without defining one. This constructor is there in the background and usually contains no commands in it. It simply creates the object and puts it in a “default state”. An object without meaning really. Our ball example would be a colorless, textureless object that has a few defined actions. By creating our own constructor we tell the object what it needs to know to become something more than a simple object. So how do we define one? Simple, in most languages it is a function with the same name as the object but without a return type. Below is our definition of a ball class. Notice the definition of a constructor.
using namespace std; // Class "ball" declaration. class ball { private: string color; string texture; public: // Constructor (same name as class) ball(string colorname, string texturename); // Public methods void setColor(string colorname); void setTexture(string texturename); string getColor(); string getTexture(); };
As you can see from the example the constructor has no return type and has the same name as the class. Also notice that this constructor is going to take two parameters, the color name and the texture name. We implement the body of this constructor function like so…
ball::ball(string colorname, string texturename) { color = colorname; texture = texturename; }
Here we are simply transferring the passed color name and the texture name to the private variables. This is the heart of throwing an object into state. In this constructor we are setting the initialization of the object to a state. So if I was to pass the object “red” and “smooth” when I create the object, the object will set its private variables to these attributes and put the object into state as a red smooth ball object.
Now often times you will see a class defined with multiple constructors. This is known as constructor overloading. The reason an object may do this is to give the programmer some options on how far they want to put the object into state. You may also see one constructor call another constructor to do part of its job of initializing. While this wasn’t done in the original C++, you do find it in languages that spawned out of C++ like Java. You can though define multiple constructors in C++ to give the programmer flexibilty.
using namespace std; // Class "ball" declaration. class ball { private: string color; string texture; public: // Multiple Constructors (all with same name as class) ball(string colorname); ball(string colorname, string texturename); // Public methods void setColor(string colorname); void setTexture(string texturename); string getColor(); string getTexture(); };
Here we are defining multiple constructors, one that takes a single color parameter and then the other one that takes two parameters. In the single parameter function body we simply set the color to whatever was passed in and manually take care of the texture by providing our own default value for it. Here is what the two constructor bodies look like. notice the difference between the two. The top one puts in the default “rough” for the texture while the other simply uses the texture provided by the user.
// Single parameter constructor ball::ball(string colorname) { color = colorname; texture = "rough"; // we set the default value to "rough" } // Multi parameter constructor ball::ball(string colorname, string texturename) { color = colorname; texture = texturename; }
With this code now the programmer has the choice of specifying only one parameter to make a rough colored ball or can make a ball which has a color and its own unique texture. They would do it like this…
int main() { // Create a red smooth ball ball *b = new ball("red","smooth"); cout << b->getColor() << "\n"; // We just specify a color of "blue" and it will be a rough blue ball. ball *c = new ball("blue"); cout << c->getColor() << "\n"; cout << c->getTexture() << "\n"; return 0; }
Now if a constructor puts an object into a known state, a destructor will take it out of that state. Do cleanup usually. Free memory and resources etc. These are usually crucial if you are going to be doing a lot of creating and destroying of objects or if your objects are really big and take a lot of memory. No need to have this huge object sitting there taking space. Instead you would free up resources whenever you can as the object is being destroyed.
The destructor is often called in many languages using the “tilde” character before the definition. It has the same name as the class again and it takes no parameters. It is meant primarily as clean up. With the introduction of the garbage collection mechanism in .NET you often don’t have to worry about cleanup of some objects. However, if your object is big and you want to make sure that it is cleaned up properly, you should define one. The garbage collection isn’t guaranteed to clean up objects immediately so unless you want a huge object sitting in memory and possibly slow down your program, it is recommended that you take care of the clean up yourself.
Here is how you define a destructor for our ball object…
// Class "ball" declaration. class ball { private: string color; string texture; public: // Multiple Constructors (all with same name as class) ball(string colorname); ball(string colorname, string texturename); // Here is our destructor (tilde, no parameters) ~ball(); // Public methods void setColor(string colorname); void setTexture(string texturename); string getColor(); string getTexture(); }; // Here is the body of the destructor. We simply print a message. ball::~ball() { cout << "Destructor called!" << "\n"; }
Now whenever one of our ball objects is destroyed, it will print a message. We can destroy the object using a “delete” function call on our object pointers in the implementation. Here is that in action…
int main() { ball *b = new ball("red","smooth"); cout << b->getColor() << "\n"; ball *c = new ball("blue"); cout << c->getColor() << "\n"; cout << c->getTexture() << "\n"; // Here we destroy both objects and get two messages delete(b); delete(c); return 0; }
So in conclusion constructors are used for putting an object into a known state. A state being a known configuration which may set its private member data and / or call helper functions to setup the object. A destructor then controls how an object is destroyed and is usually used for clean-up of objects. It might free memory, set internal pointers to null (to avoid dangling pointers) and call helper functions to dispose of objects properly. It is important that you destroy your objects properly and without relying on any garbage collection mechanism for objects that take a lot of memory or need special deconstruction.