Wednesday, September 7, 2016

Inheritance and Base Class Constructors in C++

(An update of a post made on Jan 16, 2013.)

In the 7th edition of Walls & Mirrors: C++, C++ Interlude 1 describes three types of boxes for a video game:

Plain box—a plain old box that holds only one item.
Toy box—a box that has color and holds only one item.
Magic box—a box that holds only one item, but magically changes it to the first item that was ever stored in the box.

The three classes of boxes are related by inheritance: PlainBox is the base class, and both ToyBox and MagicBox are derived from PlainBox. For simplicity, let’s just focus on the classes PlainBox and ToyBox.

Suppose that we define PlainBox as in Listing C1-3:

/** @file PlainBox.h */
#ifndef PLAIN_BOX_
#define PLAIN_BOX_
#include "BoxInterface.h"

template<class ItemType>
class PlainBox
{
private:
   ItemType item;
    
public:
   PlainBox();
   PlainBox(const ItemType& theItem);
   virtual void setItem(const ItemType& theItem);
   virtual ItemType getItem() const;
};

#include "PlainBox.cpp"
#endif

This class has a simple implementation, as you can see next and in Listing C1-4:

/** @file PlainBox.cpp */

template<class ItemType>
PlainBox::PlainBox()
{
   std::cout << "PlainBox() called" << std::endl;
} // end default constructor

template<class ItemType>
PlainBox::PlainBox(const ItemType& theItem) : item(theItem)
{
   std::cout << "PlainBox(theItem) called" << std::endl;
} // end constructor

template<class ItemType>
void PlainBox::setItem(const ItemType& theItem)
{
   item = theItem;
} // end setItem

template<class ItemType>
ItemType PlainBox::getItem() const
{
   return item;
} // end getItem

We included cout statements in the constructors so we could tell when they are called.

Now let’s derive the class ToyBox from PlainBox. An instance of ToyBox is an enhanced PlainBox object that has a color. We can write the header file for ToyBox as in Listing C1-5:

/* @file ToyBox.h */
#ifndef TOYBOX_
#define TOYBOX_
#include "PlainBox.h"

enum Color {BLACK, RED, BLUE, GREEN, YELLOW, WHITE};

template<class ItemType>
class ToyBox : public PlainBox
{
private:
   Color boxColor;
public:
   ToyBox();
   ToyBox(const Color& theColor);
   ToyBox(const ItemType& theItem, const Color& theColor);
   Color getColor() const;
}; // end ToyBox

#include "ToyBox.cpp"
#endif

Although we could provide the class with a setColor method, we choose to prevent the client from altering the color of these boxes. In particular, a ToyBox object created by the default constructor will have a default color that cannot be changed.

The constructors of ToyBox are the focus of our discussion here, so let’s review how the constructors of a derived class relate to the constructors of its base class in C++:

The derived class does not inherit the base class constructors.
Each derived class constructor can, and should, call a base class constructor.
If a derived class constructor does not explicitly call a base class constructor, C++ will call the base class default constructor for you. 

For example, the definition of ToyBox’s default constructor in Listing C1-6 is

template<class ItemType>
ToyBox::ToyBox() : boxColor(BLACK)
{
// end default constructor

This constructor implicitly calls PlainBox’s default constructor as its first step before giving a value to boxColor. The same is true of ToyBox’s second constructor:

template<class ItemType>
ToyBox::ToyBox(const Color& theColor) : boxColor(theColor) 
{
// end constructor

For either of these two constructors, we could explicitly call PlainBox’s default constructor by using an initializer as follows:

template<class ItemType>
ToyBox::ToyBox() : PlainBox(), boxColor(BLACK)
{
// end default constructor

template<class ItemType>
ToyBox::ToyBox(const Color& theColor) : PlainBox(), boxColor(theColor)
{
// end constructor

In this case,

If the C++ compiler detects an initializer call to a base class constructor, it does not add its own call to such a constructor.

There is another way for us to call a base class constructor that you should NOT use. For example, we could define ToyBox’s default constructor as

template<class ItemType>
ToyBox::ToyBox() : boxColor(BLACK)
{
   PlainBox();
// end default constructor

Although this will work, the compiler will insert its own call to PlainBox’s default constructor, because of our previous bulleted rule. We can restate this rule as

If the C++ compiler does not detect an initializer call to a base class constructor, it adds its own call to the base class’s default constructor.

If you create an instance of ToyBox using the previous constructor, you will see the result of two executions of the cout statement in PlainBox’s default constructor.

Finally, let’s define ToyBox’s third constructor, which sets both the item and color of a ToyBox object. We can call setItem to set the item:

template<class ItemType>
ToyBoxTry::ToyBoxTry(const ItemType& theItem, const Color& theColor): boxColor(theColor) 
{
   setItem(theItem);
// end constructor

Or we could use an initializer to call PlainBox’s second constructor:

template<class ItemType>
ToyBox::ToyBox(const ItemType& theItem, const Color& theColor)  
                : PlainBox(theItem), boxColor(theColor)
{
// end constructor

In the first case, PlainBox’s default constructor is called implicitly. In the second case, PlainBox’s second constructor is called explicitly by the initializer, so the compiler does not insert a call to PlainBox’s default constructor.

To summarize, we can define the implementation file for ToyBox, shown in Listing C1-6:

/* @file ToyBox.cpp */

#include “ToyBox.h”

template<class ItemType>
ToyBox::ToyBox() : boxColor(BLACK) 
{
   // Implicit call to PlainBox's default constructor
// end default constructor

template<class ItemType>
ToyBox::ToyBox(const Color& theColor) : boxColor(theColor)
{
   // Implicit call to PlainBox's default constructor
// end constructor

template<class ItemType>
ToyBox::ToyBox(const ItemType& theItem, const Color& theColor)
                : PlainBox(theItem), boxColor(theColor)
{
   // No call to PlainBox's default constructor
// end constructor

template<class ItemType>
Color ToyBox::getColor() const
{
   return boxColor;
// end getColor



Thursday, July 28, 2016

Walls and Mirrors, 6th ed., receives the 2016 McGuffey Longevity Award

On Friday June 24, my co-author and I were honored to have the 6th edition of Data Abstraction & Problem Solving: Walls and Mirrors receive the McGuffey Longevity Award from the Textbook & Academic Authors Association at their 29th annual conference, which took place in San Antonio, Texas. This award honors textbooks whose "excellence has been demonstrated over time." Indeed, the first edition of this book was published in 1995. Shortly after we learned of this honor, our 7th edition was released!

I am both excited and honored to have the 6th edition of Data Abstraction & Problem Solving with C++: Walls and Mirrors receive the 2016 TAA McGuffey Longevity Award. I share this honor with my co-author for the sixth and seventh editions, Timothy Henry, all of the people who have helped to improve the book over the course of its long life, and everyone at Pearson Education. —Frank M. Carrano
I have been incredibility fortunate to work on this textbook with Frank, and I’m grateful for his insights and guidance and for the support of the Pearson team. This recognition honors the tremendous amount work required to keep the material current and relevant. —Timothy M. Henry

Wednesday, July 27, 2016

Welcome!

I am changing platforms from Wordpress to Blogger, and so have moved from makingitreal.com to makingCSreal.com. Nothing against Wordpress, but keeping it updated on my university's server was tedious. Eventually, I'll move the more interesting posts—including my interviews with former students—from my old blog to this one. More to come!