Polymorphism Explained Part 2

Problem: Polymorphism is hard to explain and difficult to understand.

Solution: Keep the focus on polymorphism and not inheritance (which is only one way to achieve polymorphism, even if it is the most common method in statically typed languages like.

This is the promised statically typed follow up to last week’s polymorphism post (which featured Visual Basic Script).  If you did not read last week’s post please do so now.  A common problem in statically typed circles is that concept of polymorphism is very tightly coupled to the concept of inheritance.  This is understandable considering the primary method of implementing run time polymorphism is through inheritance, but I feel the concepts are best understood alone.

Like last week, lets start by looking at some simple classes without polymorphism.  The following C++ code demonstrates 3 classes representing speakers of English, Spanish, and German:

(to run the code in this post you will need to create a new Win32 Console application. The code was written for Visual C++, but can be easily adapted for any C++ compiler)


// Classes in C++ (C++ Version Of Classes In VBS)
#include "stdafx.h"
#include <iostream>
#include <memory>

using namespace std;

// Define Class For English Speaker
class EnglishSpeaker
{
public:
   void SayHello()
   {
      cout << "English Speaker Says \"Hello\"." << endl;
   }

   void SayGoodbye()
   {
      cout << "English Speaker Says \"Goodbye\"." << endl;
   }
};

// Define Class For Spanish Speaker
class SpanishSpeaker
{
public:
   void SayHello()
   {
      cout << "Spanish Speaker Says \"Hola\"." << endl;
   }

   void SayGoodbye()
   {
      cout << "Spanish Speaker Says \"Adios\"." << endl;
   }
};

// Define Class For German Speaker
class GermanSpeaker
{
public:
   void SayHello()
   {
      cout << "German Speaker Says \"Hallo\"." << endl;
   }

   void SayGoodbye()
   {
      cout << "German Speaker Says \"Auf Wiedersehen\"." << endl;
   }
};

void main()
{
   // Create An Instance Of EnglishSpeaker
   unique_ptr<EnglishSpeaker> englishMan(new EnglishSpeaker);

   // Create An Instance Of SpanishSpeaker
   unique_ptr<SpanishSpeaker> spanishMan(new SpanishSpeaker);

   // Create An Instance Of GermanSpeaker
   unique_ptr<GermanSpeaker> germanMan(new GermanSpeaker);

   // All Say Hello
   englishMan->SayHello();
   spanishMan->SayHello();
   germanMan->SayHello();

   // All Say Goodbye
   englishMan->SayGoodbye();
   spanishMan->SayGoodbye();
   germanMan->SayGoodbye();

   // Wait For User To Press Enter To End The Program
   cout << endl << "Please press enter to close the program ...";
   cin.get();
}

Again like last week, lets create a version that allows the Speaker to be selected at run time.  We will call the new speaker a polyglot (multilingual) speaker.  The first thing we need to do is to create a base class that each of the other classes can inherit from.  We will call this base class ISpeaker (because basically ISpeaker is an interface, which is implemented in C++ as a pure abstract class.

// Define Interface (Pure Abstract Class) For Speaker
class ISpeaker
{
public:
	virtual void SayHello() = 0;
	virtual void SayGoodbye() = 0;
};

In C++ a pure abstract class is a class that only contains abstract functions.  An abstract function is a function that is defined with virtual and ” = 0;” at the end (such as the above SayHello() and SayGoodbye() functions.  A pure abstract class can not be instantiated, thus it can only be used as a pointer to a child class.

You will notice that each of the speaker classes have also been modified to inherit from the ISpeaker class.  The syntax can be seen below in the EnglishSpeaker class:

// Define Class For English Speaker
class EnglishSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "English Speaker Says \"Hello\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "English Speaker Says \"Goodbye\"." << endl;
	}
};

Notice that the “class EnglishSpeaker” is now followed by ” : public ISpeaker”.  This makes EnglishSpeaker a child class of ISpeaker.  You will also notice that each of the functions are now marked virtual, which is required for polymorphic functions in C++.

I decided to include two polymorphic implementations for C++.  The first is the old style C++ that requires manual memory management:

// Polymorphism in C++ (Old C++ Version Of Polymorphism In VBS)
//

#include "stdafx.h"
#include <iostream>
#include <memory>
#include <string>
#include <utility>

using namespace std;

// Define Interface (Pure Abstract Class) For Speaker
class ISpeaker
{
public:
	virtual void SayHello() = 0;
	virtual void SayGoodbye() = 0;
};

// Define Class For English Speaker
class EnglishSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "English Speaker Says \"Hello\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "English Speaker Says \"Goodbye\"." << endl;
	}
};

// Define Class For Spanish Speaker
class SpanishSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "Spanish Speaker Says \"Hola\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "Spanish Speaker Says \"Adios\"." << endl;
	}
};

// Define Class For German Speaker
class GermanSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "German Speaker Says \"Hallo\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "German Speaker Says \"Auf Wiedersehen\"." << endl;
	}
};

void main()
{
	// Create A Pointer To Ployglot Speaker
	ISpeaker* polyglotSpeaker = null;

	// Loop To Demonstrate Polyglot
	string repeatSelection;
	do
	{
		// Prompt User For Desired Language
		cout << "Please select a desired language for the polyglot speaker" << endl;
		cout << "  (E=English, S=Spanish, G=German):";
		string desiredLanguage;
		cin >> desiredLanguage;
		desiredLanguage = toupper(desiredLanguage[0]);

		// Create A Polyglot Speaker (Multilingual Speaker)
		// (NOTE: The polyglot is really just one of the above speakers, but selected at runtime)
		if (desiredLanguage == "E")
		{
			polyglotSpeaker = new EnglishSpeaker;
		}
		else if (desiredLanguage == "S")
		{
			polyglotSpeaker = new SpanishSpeaker;
		}
		else if (desiredLanguage == "G")
		{
			polyglotSpeaker = new GermanSpeaker;
		}
		else
		{
			polyglotSpeaker = null
			cout << "Invalid Language Selection For Polyglot Speaker" << endl;
		}

		// Check If Polyglot Speaker Was Created (i.e. a valid language was selected)
		if (polyglotSpeaker != null)
		{
			// Say Hello With Polyglot Speaker
			polyglotSpeaker->SayHello();

			// Say Goodbye With Polyglot Speaker
			polyglotSpeaker->SayGoodbye();
		}

		// Release Polyglot Speaker
		// (note, that to be exception safe this would require the above code to be in a try/catch block)
		if (polyglotSpeaker != null)
		{
			delete polyglotSpeaker;
			polyglotSpeaker = null;
		}

		// Check If Should Continue
		cout << endl << "Repeat Polyglot Language Selection? (Y=Yes, N=No)";
		cin >> repeatSelection;
		repeatSelection = toupper(repeatSelection[0]);
		cout << endl;
	} while (repeatSelection == "Y");

	// Wait For User To Press Enter To End The Program
	cout << endl << "Please press enter to close the program ...";
	cin.get();
}

As you can see the statically typed version is not that different from the dynamically typed version from last week.  (of course the C++ syntax is different, but the concepts are very similar).  The biggest conceptual difference is that the C++ version verifies the names at compile time (thus is statically typed).  Additionally, because the C++ version will use virtual tables internally it runs a lot faster.

Here is the second polymorphic version for C++ that uses the new unique_ptr smart pointer wrapper class.  This version does not require manual memory management, because the unique_ptr class implements RAII (Resource Acquisition Is Initialization).  If you do not understand RAII then I recommend you study it carefully as it can really improve your code.  Here is the second version:

// Polymorphism v2 in C++ (Modern C++ Version Of Polymorphism In VBS)
//

#include "stdafx.h"
#include <iostream>
#include <memory>
#include <string>
#include <utility>

using namespace std;

// Define Interface (Pure Abstract Class) For Speaker
class ISpeaker
{
public:
	virtual void SayHello() = 0;
	virtual void SayGoodbye() = 0;
};

// Define Class For English Speaker
class EnglishSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "English Speaker Says \"Hello\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "English Speaker Says \"Goodbye\"." << endl;
	}
};

// Define Class For Spanish Speaker
class SpanishSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "Spanish Speaker Says \"Hola\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "Spanish Speaker Says \"Adios\"." << endl;
	}
};

// Define Class For German Speaker
class GermanSpeaker : public ISpeaker
{
public:
	virtual void SayHello()
	{
		cout << "German Speaker Says \"Hallo\"." << endl;
	}

	virtual void SayGoodbye()
	{
		cout << "German Speaker Says \"Auf Wiedersehen\"." << endl;
	}
};

void main()
{
	// Create A Pointer To Ployglot Speaker
	// (unique_ptr is a smart pointer wrapper that provides automatic memory management)
	unique_ptr<ISpeaker> polyglotSpeaker;

	// Loop To Demonstrate Polyglot
	string repeatSelection;
	do
	{
		// Prompt User For Desired Language
		cout << "Please select a desired language for the polyglot speaker" << endl;
		cout << "  (E=English, S=Spanish, G=German):";
		string desiredLanguage;
		cin >> desiredLanguage;
		desiredLanguage = toupper(desiredLanguage[0]);

		// Create A Polyglot Speaker (Multilingual Speaker)
		// (NOTE: The polyglot is really just one of the above speakers, but selected at runtime)
		if (desiredLanguage == "E")
		{
			polyglotSpeaker.reset(new EnglishSpeaker);
		}
		else if (desiredLanguage == "S")
		{
			polyglotSpeaker.reset(new SpanishSpeaker);
		}
		else if (desiredLanguage == "G")
		{
			polyglotSpeaker.reset(new GermanSpeaker);
		}
		else
		{
			polyglotSpeaker = move(nullptr);
			cout << "Invalid Language Selection For Polyglot Speaker" << endl;
		}

		// Check If Polyglot Speaker Was Created (i.e. a valid language was selected)
		if (polyglotSpeaker.get() != nullptr)
		{
			// Say Hello With Polyglot Speaker
			polyglotSpeaker->SayHello();

			// Say Goodbye With Polyglot Speaker
			polyglotSpeaker->SayGoodbye();
		}

		// Don't Need To Release Polyglot Speaker (unique_ptr will handle this, even if exceptions)

		// Check If Should Continue
		cout << endl << "Repeat Polyglot Language Selection? (Y=Yes, N=No)";
		cin >> repeatSelection;
		repeatSelection = toupper(repeatSelection[0]);
		cout << endl;
	} while (repeatSelection == "Y");

	// Wait For User To Press Enter To End The Program
	cout << endl << "Please press enter to close the program ...";
	cin.get();
}

As you can see there is very little difference between the first and second C++ versions.  This is because unique_ptr does a very good job of simulating a real pointer.  I highly recommend using unique_ptr when appropriate to make memory management much easier.

I hope this post helped you understand polymorphism a little bit better. I plan to follow up with some more practical examples of polymorphism, so stay tuned.

// Classes in C++ (C++ Version Of Classes In VBS)
//#include “stdafx.h”
#include <iostream>
#include <memory>using namespace std;// Define Class For English Speaker
class EnglishSpeaker
{
public:
void SayHello()
{
cout << “English Speaker Says \”Hello\”.” << endl;
}void SayGoodbye()
{
cout << “English Speaker Says \”Goodbye\”.” << endl;
}
}; 

// Define Class For Spanish Speaker
class SpanishSpeaker
{
public:
void SayHello()
{
cout << “Spanish Speaker Says \”Hola\”.” << endl;
}

void SayGoodbye()
{
cout << “Spanish Speaker Says \”Adios\”.” << endl;
}
};

// Define Class For German Speaker
class GermanSpeaker
{
public:
void SayHello()
{
cout << “German Speaker Says \”Hallo\”.” << endl;
}

void SayGoodbye()
{
cout << “German Speaker Says \”Auf Wiedersehen\”.” << endl;
}
};

void main()
{
// Create An Instance Of EnglishSpeaker
unique_ptr<EnglishSpeaker> englishMan(new EnglishSpeaker);

// Create An Instance Of SpanishSpeaker
unique_ptr<SpanishSpeaker> spanishMan(new SpanishSpeaker);

// Create An Instance Of GermanSpeaker
unique_ptr<GermanSpeaker> germanMan(new GermanSpeaker);

// All Say Hello
englishMan->SayHello();
spanishMan->SayHello();
germanMan->SayHello();

// All Say Goodbye
englishMan->SayGoodbye();
spanishMan->SayGoodbye();
germanMan->SayGoodbye();

// Wait For User To Press Enter To End The Program
cout << endl << “Please press enter to close the program …”;
cin.get();
}

About dcravey

Computer Programmer
This entry was posted in Programming. Bookmark the permalink.

1 Response to Polymorphism Explained Part 2

  1. Pingback: Tomato Blog

Leave a comment