Creating objects
Declaring variables
Consider the following variable declarations.
int length;
int area = 0;
DayOfYear today;
What happens "behind the scenes" in our program?
Is that what we want to happen?
Can we control what happens?
Constructor Basics
A constructor is a special kind of member function
that is used to create (construct) new objects. A constructor is
automatically called whenever an object is created. It is used to
initialize the values of some or all data members or perform any other
type of initialization that the object requries.
We can add a constructor to our DayOfYear class.
class DayOfYear
{
public:
// Constructor
DayOfYear (int initialMonth, int initialDay);
void Input( );
void Output( );
// other public methods
private:
int m_month;
int m_day;
};
Note that the constructor
- Has the same name as the class
- Has no return type (not even void)
// we can now declare DayOfYear variables to
// create DayOfYear objects like this
DayOfYear christmas( 12, 25 );
DayOfYear july4th( 7, 4 );
// or even like this
int month = 5;
int day = 12;
DayOfYear someDay( month, day);
The definition of a constructor is given in the same way
as any other member function. Once again note that the
constructor has the same name as the class and has no
return type.
DayOfYear::DayOfYear( int initialMonth, int initialDay )
{
m_month = initialMonth;
m_day = initialDay;
}
An alternative way of defining constructors is also possible.
This method uses the member initialization list.
This method is sometimes necessary (we'll see why later) and
so is the preferred method, even when not required.
DayOfYear::DayOfYear( int initialMonth, int initialDay )
:m_month( initialMonth ), m_day( initialDay )
{
// no code
}
The body of this style of constructor need not be empty, but
if left empty, good programming practice requires an explicit
comment.
Overloaded constructors
Since a constructor is a member function, a constructor can be
(and often is) overloaded. Consider this new version of
the DayOfYear class.
class DayOfYear
{
public:
// specify the month and day as above
DayOfYear (int initialMonth, int initialDay);
// initialize to first (day=1) of the month
DayOfYear (int initialMonth);
// initialize to January 1
// This is the "default" constructor
// It is the only one which can be called with no paramters
DayOfYear ( );
void Input( );
void Output( );
// other public methods
private:
int m_month;
int m_day;
};
//--------------------------------
// DayOfYear constructor
// PreConditions:
// 1 <= month <= 12
// PostConditions:
// new object created with specified
// month and day = 1
//------------------------------------
DayOfYear::DayOfYear( int month )
: m_month( month ), m_day( 1 )
{
// no code
}
//--------------------------------
// DayOfYear Default constructor
// PreConditions:
// none
// PostConditions:
// new object created with
// month = 1 and day = 1
//------------------------------------
DayOfYear::DayOfYear( )
: m_month( 1 ), m_day( 1 )
{
// no code
}
We can now create DayOfYear objects in a variety of ways
DayOfYear christmas( 12, 25 );
DayOfYear aprilFools ( 4 );
// this declaration uses the default constructor
DayOfYear jan1st;
// but NOT like this, even though it's
// tempting and compiles without errors
DayOfYear today( );
Getting a little fancy
Since constructors are functions, not only can they be overloaded
as we've seen, they can also have default parameters. In fact,
one properly defined constructor using default parameters can
take the place of the three constructors defined above. This mechanism
gives the user flexibility without requiring the implementer to duplicate
code. Consider this new class definition of DayOfYear.
class DayOfYear
{
public:
// this constructor can be invoked in three ways
// and is also the default constructor
DayOfYear (int initialMonth = 1, int initialDay = 1);
void Input( );
void Output( );
// other public methods
private:
int m_month;
int m_day;
};
Each of these declarations invokes the one and
only constructor for the new DayOfYearClass
DayOfYear someDay; // month = 1 and day = 1 via default parameters
DayOfYear march1st( 3 ); // explicit month = 3; day = 1 by default
DayOfYear july4th( 7, 4 ); // explicit month and day
Why haven't we seen this before?
Up until now, we have not provided any constructors for our classes. In this case,
the compiler provides a default constructor for our class. This default constructor has
been sufficient for our needs so far.
A couple of interesting notes
- If you write any constructor for your class, you are no longer provided
a default constructor by the compiler.
- When declaring a vector of objects such as vector<Bob> group( 20 );,
the compiler automatically invokes the default constructor for each object in
the vector
What if something goes wrong?
Our DayOfYear constructor has some pre-conditions, but our example code didn't
test them....we've been focusing on the concept and syntax.
But what should happen if the DayOfYear object we are asked to create is
invalid -- if the month is greater than 12? if the day is negative? if the day is
not valid for the specified month (eg Februrary 30th)? In such cases, we need to
alert the user that the object is in an invalid state. In more sophisticated situations,
the object could be only partially constructed. Such objects are sometimes called "zombies".
How do we alert the user to this situation.
For time being, all we can do is to provide the user with a member function
-- IsValid( )he can call to ascertain the state of the object.
This function could be an accessor for
a new private boolean data member (m_isValid) which is set by the constructor,
or could decide the validity of the object when called.
Our complete constructor would look something like this
DayOfYear::DayOfYear( int initialMonth, int initialDay )
:m_month( initialMonth ), m_day( initialDay )
{
if (m_month < 1 || m_month > 12)
m_isValid = false;
else if ( m_day < 1 || m_day > 31)
m_isValid = false;
else if ( day is too big for the specified month)
m_isValid = false
else
m_isValid = true;
}
In this case, the member function isValid( ) is just an accessor
for m_isValid
bool DayOfYear::IsValid( )
{
return m_isValid;
}
Now the user can construct DayOfYear objects and check their validity in case
there is any doubt
DayOfYear futureDay (month, day);
if (!futureDay.IsValid( ))
{
// user decides what to do
}
Last Modified: Monday, 28-Aug-2006 10:15:53 EDT