Constructors

Steven Zeil

Last modified: Dec 26, 2016
Contents:

1 Initializing Structured Data

1.1 Constructors

1.2 Naming Constructors

1.3 Calling Constructors

A constructor can be called like a normal function:

if (myTime.noLaterThan(Time(16,15,00)))
   cout << "Class is not yet over" << endl;

1.4 Calling Constructors in Declarations

More often, a constructor is called within a declaration:

Time midnight; // calls Time()
Time startOfClass (15, 00, 00); // calls Time(h,m,s)
Time endOfClass {16, 15, 00}; // calls Time(h,m,s)

(The use of { } in constructor calls is new to C++11 and might not be supported by all compilers yet.)

1.5 Some Special Syntax for Declarations

A few special rules:

1.6 What You’ve Been Doing All Along

You’ve written or read declarations like the ones below.

string s;
string s1 (s);
string s2 = "abc";
string s3 (5, z); // "zzzzz"
ifstream in ("myFileName.txt");

2 Implementing Constructors

The primary purpose of a constructor is to initialize a structured data value.

struct BidderSequence {
  static const int capacity = 1000;
  int size;
  Bidder data[capacity];

  // Initialize a sequence with size 0
  BidderSequence();
};
   ⋮
BidderSequence::BidderSequence()
{
  size = 0;
}

Another example:

struct Money {
  int dollars;
  int cents;

  /**
  * Initialize a monetary amount. Default is $0.0
  */
  Money (int dollarPart = 0, int centsPart = 0);
    ⋮
};
    ⋮
Money::Money (int dollarPart, int centsPart)
{
  dollars = dollarPart;
  cents = centsPart;
}

2.1 Data Must be Initialized Before Use

Forgetting to initialize data is one of the most common mistakes that programmers make.

Without constructors, it’s all too easy to do something like

BidderSequence seq1, seq2;
  ⋮
seq1.size = 0;
// code that uses seq1
  ⋮
// code that uses seq2
  ⋮

forgetting that seq2 has not yet been properly initialized.

2.2 Constructors Help to Protect You

Once we introduce constructor functions, however, our variables get initialized the moment we declare them.

BidderSequence::BidderSequence ()
{
  size = 0;
}
  ⋮
BidderSequence seq1, seq2;
// Both variables are immediately
// initialized with size = 0

2.3 Overloading Constructors

Like other functions, constructors can be overloaded, providing multiple functions with the same name but different parameter types.

struct Item {
  std::string name;
  Money reservedPrice;
  Time auctionEndsAt;
  
  /**
  * Initialize an item to empty name, no reserve,
  * and with auction ending at midnight.
  */
  Item();
  
  /** Initialize an item */
  Item (std::string iName, Money reserved, Time endsAt);
    ⋮
};

2.4 Overloading Constructors (implem.)

/**
 * Initialize an item to empty name, no reserve, with auction
 * ending at midnight.
 */
Item::Item()
{  
  name = "";
  reservedPrice = Money(0,0);
  auctionEndsAt = Time(0,0,0);
}

/**  Initialize an item  */
Item::Item (std::string iName, Money reserved, Time endsAt)
{
  name = iName;
  reservedPrice = reserved;
  auctionEndsAt = endsAt;
}

3 The Default Constructor

The default constructor is a constructor that takes no arguments. This is the constructor you are calling when you declare an object with no parameters. E.g.,

std::string s;

3.1 Declaring a Default Constructor

It might be declared like this

class Time {
public:
   Time();
    ⋮

or with defaults:

namespace std {
class string {
public:
    ⋮
   string(char* s = "");
   ⋮

Either way, we can call it with no parameters.

3.2 Why ‘default’?

3.3 Implicit Use: Arrays

For example, if we declared:

std::string words[5000];

then each of the 5000 elements of this array will be initialized using the default constructor for string

3.4 Implicit Use: Other Constructors

Earlier, we had:

/**
 * Initialize an item to empty name, no reserve, with auction
 * ending at midnight.
 */
Item::Item()
{  
  name = "";
  reservedPrice = Money(0,0);
  auctionEndsAt = Time(0,0,0);
}

But, consider that


Implicit Use: Other Constructors (cont.)

So we might as well write:

Item::Item()
{  
}

rather than

Item::Item()
{  
  name = "";
  reservedPrice = Money(0,0);
  auctionEndsAt = Time(0,0,0);
}

It’s shorter and faster!


Think About This

For our type

struct BidderSequence {
   static const int capacity = 1000;
   int size;
   Bidder data[capacity];

   BidderSequence();
     ⋮
};

we had the constructor

BidderSequence::BidderSequence ()
{
  size = 0;
}

Is the data initialized?

Answer: yes. because data is an array of Bidder, each element in the array is initialized using the Bidder default constructor.

In fact, I had originally planned not to give Bidder a default constructor, because the auction program did not have an obvious use for a “default” bidder. But the moment I wrote the BidderSequence declaration above, I remembered that I could not have an array of Bidders if I did not have a Bidder default constructor.

4 Initialization Lists

What if we have data members that we want to initialize with a non-default value?

struct Item {
   std::string name;
   Money reservedPrice;
   Time auctionEndsAt;


   /**
    * Initialize an item to empty name, no reserve, 
    * with auction ending at midnight.
    */
   Item();
    ⋮

Suppose that we wanted a default item’s reserve price to be one dollar and the auction to end a second before midnight?

4.1 One Approach: Re-assignment

We could do

Item::Item()
{
  reservedPrice = Money(1,0);  
  auctionEndsAt = Time(23,59,59);
}

But this is inefficient …

4.2 Initialization List

Instead we can tell the compiler how we want to initially construct the data members:

itemInitList.cpp
Item::Item()
  : reservedPrice(1,0), auctionEndsAt(23,59,59)
{
}

4.3 Interpeting Initialization Lists

itemInitList.cpp
Item::Item()
  : reservedPrice(1,0), auctionEndsAt(23,59,59)
{
}

5 Compiler-Generated Default Constructors

If we create no constructors at all for a class, the compiler generates a default constructor for us.


Example: Name

class Name {
public:
   string getGivenName();
   void setGivenName (string);

   string getSurName();
   void setSurName (string);
private:
   string givenName;
   string surName;
};


Example: Name 2

class Name {
public:
   Name (string gName, string sName)
     : givenName(gName), surName(sName) {}

   string getGivenName();
   void setGivenName (string);

   string getSurName();
   void setSurName (string);
private:
   string givenName;
   string surName;
};