Section 2 - Understanding Constructors
6B.2.1 The Class Constructor
In the main() method, we instantiate two Galaxy objects:
Galaxy gal1, gal2;
At this point, what values are stored in the private data name and magnitude? Since the client, main(), has not manually set these yet, it is unclear what would happen if we tried to print out the names and magnitudes of gal1 and gal2.
Initializing the member data of our class is the job of the class constructor. The constructor is a method that
- bears the same name as that of the class, and
- has no return type (it is not void, int, double, etc.)
The constructor method is never called explicitly but is always called automatically for you when a new object is instantiated. In other words the statement:
Galaxy gal1;
causes the Galaxy constructor to be invoked. After that, we can be sure that gal1's fields have some minimum, usable data in them. If you look at the constructor, you can see the reasonable values that we use to initialize the two members.
// default constructor Galaxy::Galaxy() { name = "undefined"; magnitude = 0.0; }
Now, if the mutator methods setName() or setMagnitude() fail to set the private members name and magnitude due to poor candidates passed in by the client, these fields will at least contain their default values.
6B.2.2 Default Constructors vs. Constructors Taking Parameters
This above constructor is called a default constructor because it takes no parameters. However, you can give your constructor formal parameters if you wish. For example, you might create a constructor for Galaxy that takes two parameters:
// 2-parameter constructor Galaxy::Galaxy(string myName, double myMag) { if (myName.length() > 1) name = myName; else name = "undefined"; if (myMag >= -3 && myMag <= 30) magnitude = myMag; else magnitude = 0.0; }
When a constructor takes parameters, it, like the mutator, should check for bad values before setting the private data. That's why we have the tests inside the constructor above. However, in a couple pages we'll see a better way for the constructor to do this. Meanwhile, the above example adequately demonstrates one way that a constructor might protect the private data from bad user-passed values. In your homework, you will not do it this way. Instead you will use the technique demonstrated in a couple pages using mutators.
Also, we can have both default constructors and constructors that take parameters. Just like we have seen overloaded methods in the past, there is nothing preventing us from overloading constructors (review method overloading if you forgot that concept). Multiple constructors, each taking a different number and/or types of parameters, is nothing more than method overloading applied to a constructor.
If we provide the constructor, above, that takes two parameters, we might instantiate Galaxy objects like so:
Galaxy gal1("M31", 8.9), gal2("Andromeda", 3.5);
If a constructor takes only one parameter, then we can use a special syntax for passing an argument to the constructor. Imagine a Galaxy constructor that only took the name as a parameter:
// 1-parameter constructor Galaxy::Galaxy(string myName) { if (myName.length() > 0) name = myName; else name = "undefined"; magnitude = 0.0; }
We can use the = symbol instead of functional notation in the instantiation. Either of the following initializations work:
Galaxy gal1("M31"), gal2 = "Andromeda";