Skip to content

Instantly share code, notes, and snippets.

@simonayzman
Created September 14, 2017 16:02
Show Gist options
  • Save simonayzman/30a6b4ad50572f6fa4dbbcb00e600584 to your computer and use it in GitHub Desktop.
Save simonayzman/30a6b4ad50572f6fa4dbbcb00e600584 to your computer and use it in GitHub Desktop.
Applying Polymorphism to Data in Data Structures
#include <iostream>
#include <vector>
using namespace std;
class Residence {
public:
Residence(double newPrice = 1.0) : price(newPrice) {}
virtual ~Residence() {}
double getPrice() { return price; }
virtual double valuation() { return 1.0; }
virtual string typeResidence() { return "NO TYPE"; }
virtual double taxRate() { return 0.10; }
virtual void display() {
cout << typeResidence()
<< ".\t Price: " << price
<< ".\t Tax rate: " << taxRate()
<< ".\t Valuation: " << valuation() << ".\n";
}
protected:
double price;
};
class Coop: public Residence {
public:
Coop(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (.5*price+20); }
virtual string typeResidence() { return "Coop"; }
};
class Condo: public Residence {
public:
Condo(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (1.2*price); }
virtual string typeResidence() { return "Condo"; }
virtual double taxRate() { return 0.18; }
};
class House: public Residence {
public:
House(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (price+50); }
virtual string typeResidence() { return "House"; }
};
const unsigned int NUM_RESIDENCES = 10;
void basicPolymorphismTest() {
// You can use this Residence pointer to point to any other object that "is-a" Residence.
// Alternatively, it can also point to dynamically allocated Residences (subclasses included).
Residence* generalResidencePointer;
Residence residenceObject;
generalResidencePointer = &residenceObject;
generalResidencePointer->display(); // Polymorphism works!
Residence* residencePointer = new Residence(2.0);
generalResidencePointer = residencePointer;
generalResidencePointer->display(); // Polymorphism works!
delete residencePointer;
Coop coopObject(100.0);
generalResidencePointer = &coopObject;
generalResidencePointer->display(); // Polymorphism works!
Coop* coopPointer = new Coop(200.0);
generalResidencePointer = coopPointer;
generalResidencePointer->display(); // Polymorphism works!
delete coopPointer;
Condo condoObject(300.0);
generalResidencePointer = &condoObject;
generalResidencePointer->display(); // Polymorphism works!
Condo* condoPointer = new Condo(400.0);
generalResidencePointer = condoPointer;
generalResidencePointer->display(); // Polymorphism works!
delete condoPointer;
House houseObject(500.0);
generalResidencePointer = &houseObject;
generalResidencePointer->display(); // Polymorphism works!
House* housePointer = new House(600.0);
generalResidencePointer = housePointer;
generalResidencePointer->display(); // Polymorphism works!
delete housePointer;
}
void basicPolymorphismTest_StoringObjects_StaticBindingProblem() {
// Here, we will have a problem. If you have a vector of Residence
// objects then you will not have the benefit of polymorphism.
vector<Residence> residenceObjects;
cout << "\n\nInitializing Residence object vector\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
// Create a random price fom $50 to $2000
double randomPrice = (rand() % 19 + 1) * 50;
// Choose a random type of residence to create
int typeResidence = rand() % 4;
switch (typeResidence) {
case 0: {
Coop coop(randomPrice);
residenceObjects.push_back(coop);
break;
}
case 1: {
Condo condo(randomPrice);
residenceObjects.push_back(condo);
break;
}
case 2: {
House house(randomPrice);
residenceObjects.push_back(house);
break;
}
case 3: {
Residence residence(randomPrice);
residenceObjects.push_back(residence);
break;
}
}
}
// No matter which way you try to access the Residence objects in the vector, C++ will only see
// them as Residence objects. We declared it as such, so when we pushed back any Residence
// child into the vector, the assignment operator (=) effectively took away any child specialization.
cout << "Iterating over Residence object vector (accessing as objects)\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
Residence currentResidence = residenceObjects[i];
currentResidence.display();
}
cout << "Iterating over Residence object vector (accessing with pointers)\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
Residence* currentResidence = &(residenceObjects[i]);
currentResidence->display();
}
cout << "No polymorphism, regardless how you access the Residence objects!\n";
}
void basicPolymorphismTest_StoringObjectPointers_Solution() {
// Here, we will solve the problem! If you have a vector of pointers to Residence objects, then
// you WILL reap the benefit of polymorphism (assuming some functions are qualified with virtual)
vector<Residence*> residencePointers;
cout << "\n\nInitializing Residence pointer vector\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
// Create a random price fom $50 to $2000
double randomPrice = (rand() % 19 + 1) * 50;
// Choose a random type of residence to create
int typeResidence = rand() % 4;
switch (typeResidence) {
case 0: {
Coop* coop = new Coop(randomPrice);
residencePointers.push_back(coop);
break;
}
case 1: {
Condo* condo = new Condo(randomPrice);
residencePointers.push_back(condo);
break;
}
case 2: {
House* house = new House(randomPrice);
residencePointers.push_back(house);
break;
}
case 3: {
Residence* residence = new Residence;
residencePointers.push_back(residence);
break;
}
}
}
cout << "Iterating over Residence object vector (accessing with pointers)\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
Residence* currentResidence = residencePointers[i];
currentResidence->display();
}
cout << "Polymorphism here works!\n";
}
int main() {
srand((unsigned int)time(NULL));
basicPolymorphismTest();
basicPolymorphismTest_StoringObjects_StaticBindingProblem();
basicPolymorphismTest_StoringObjectPointers_Solution();
return 0;
}
#include <iostream>
#include <vector>
using namespace std;
class Residence {
public:
Residence(double newPrice = 0.0) : price(newPrice) {}
virtual ~Residence() {}
double getPrice() { return price; }
virtual double valuation() = 0; // Pure virtual! This is an abstract class.
virtual string typeResidence() = 0; // Pure virtual! This is an abstract class.
virtual double taxRate() { return 0.10; }
virtual void display() {
cout << typeResidence()
<< ". Price: " << price
<< ". Tax rate: " << taxRate()
<< ". VALUATION: " << valuation() << ".\n";
}
protected:
double price;
};
class Coop: public Residence {
public:
Coop(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (.5*price+20); }
virtual string typeResidence() { return "Coop"; }
};
class Condo: public Residence {
public:
Condo(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (1.2*price); }
virtual string typeResidence() { return "Condo"; }
virtual double taxRate() { return 0.18; }
};
class House: public Residence {
public:
House(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (price+50); }
virtual string typeResidence() { return "House"; }
};
const unsigned int NUM_RESIDENCES = 10;
void basicPolymorphismTest() {
// This is legal. You can use this to point to other Residence (subclass) objects.
// Or you can dynamically allocate subclass instances and point to them.
Residence* generalResidencePointer;
// This comented out portion is illegal; it won't compile. You can neither create direct Residence
// objects, nor can you dynamically allocate pointers to Residence objects.
/*
Residence residenceObject;
generalResidencePointer = &residenceObject;
generalResidencePointer->display(); // Polymorphism works!
Residence* residencePointer = new Residence(2.0);
generalResidencePointer = residencePointer;
generalResidencePointer->display(); // Polymorphism works!
delete residencePointer;
*/
Coop coopObject(100.0);
generalResidencePointer = &coopObject;
generalResidencePointer->display(); // Polymorphism works!
Coop* coopPointer = new Coop(200.0);
generalResidencePointer = coopPointer;
generalResidencePointer->display(); // Polymorphism works!
delete coopPointer;
Condo condoObject(300.0);
generalResidencePointer = &condoObject;
generalResidencePointer->display(); // Polymorphism works!
Condo* condoPointer = new Condo(400.0);
generalResidencePointer = condoPointer;
generalResidencePointer->display(); // Polymorphism works!
delete condoPointer;
House houseObject(500.0);
generalResidencePointer = &houseObject;
generalResidencePointer->display(); // Polymorphism works!
House* housePointer = new House(600.0);
generalResidencePointer = housePointer;
generalResidencePointer->display(); // Polymorphism works!
delete housePointer;
}
void basicPolymorphismTest_StoringObjects_StaticBindingProblem() {
// This entire function is no longer relevant. You cannot create a vector of Residence objects at
// all. The vector will create capacity for some unknown number of spots, but in doing so, must
// "create" Residence object placeholders until the vector actually gets filled. "Creating" the
// object means calling the constructor. You can't do that with the abstract class Residence.
/*
vector<Residence> residenceObjects;
cout << "\n\nInitializing Residence object vector\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
// Create a random price fom $50 to $2000
double randomPrice = (rand() % 19 + 1) * 50;
// Choose a random type of residence to create
int typeResidence = rand() % 4;
switch (typeResidence) {
case 0: {
Coop coop(randomPrice);
residenceObjects.push_back(coop);
break;
}
case 1: {
Condo condo(randomPrice);
residenceObjects.push_back(condo);
break;
}
case 2: {
House house(randomPrice);
residenceObjects.push_back(house);
break;
}
case 3: {
Residence residence(randomPrice);
residenceObjects.push_back(residence);
break;
}
}
}
cout << "Iterating over Residence object vector (accessing as objects)\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
Residence currentResidence = residenceObjects[i];
currentResidence.display();
}
cout << "Iterating over Residence object vector (accessing with pointers)\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
Residence* currentResidence = &(residenceObjects[i]);
currentResidence->display();
}
*/
}
void basicPolymorphismTest_StoringObjectPointers_Solution() {
// This is perfectly legal. You can easily create a vector of pointers; "creating" a pointer, by
// default, sets the pointer to NULL, so there is no concern about the Residence constructor
// being called. You must make sure, however, that you do not try to allocate any Residence
// objects or dynamically allocate any (direct) Residence pointers. This would be illegal
vector<Residence*> residencePointers;
cout << "\n\nInitializing Residence pointer vector\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
// Create a random price fom $50 to $2000
double randomPrice = (rand() % 19 + 1) * 50;
// Choose a random type of residence to create
int typeResidence = rand() % 3;
switch (typeResidence) {
case 0: {
Coop* coop = new Coop(randomPrice);
residencePointers.push_back(coop);
break;
}
case 1: {
Condo* condo = new Condo(randomPrice);
residencePointers.push_back(condo);
break;
}
case 2: {
House* house = new House(randomPrice);
residencePointers.push_back(house);
break;
}
// This entire case is no longer legal
/*
case 3: {
Residence* residence = new Residence;
residencePointers.push_back(residence);
break;
}
*/
}
}
cout << "Iterating over Residence object vector (accessing with pointers)\n";
for (int i = 0; i < NUM_RESIDENCES; ++i) {
Residence* currentResidence = residencePointers[i];
currentResidence->display();
}
}
int main() {
srand((unsigned int)time(NULL));
basicPolymorphismTest();
basicPolymorphismTest_StoringObjects_StaticBindingProblem();
basicPolymorphismTest_StoringObjectPointers_Solution();
return 0;
}
#include <iostream>
#include "SortedVector.h" // Found in https://github.com/CSCI235-Ayzman/SortedStructures
#include "SortedLinkedList.h" // Found in https://github.com/CSCI235-Ayzman/SortedStructures
using namespace std;
class Residence {
public:
Residence(double newPrice = 0.0) : price(newPrice) {}
virtual ~Residence() {}
double getPrice() { return price; }
virtual double valuation() = 0; // Pure virtual! This is an abstract class.
virtual string typeResidence() = 0; // Pure virtual! This is an abstract class.
virtual double taxRate() { return 0.10; }
virtual void display() {
cout << typeResidence()
<< ". Price: " << price
<< ". Tax rate: " << taxRate()
<< ". VALUATION: " << valuation() << ".\n";
}
// One Residence is considered "smaller" than another if it has a smaller valuation
// You'll notice that this allows us to compare different kinds of Residences
// (aka, subclasses of Residences) even though valuation() is pure virtual
friend bool operator<(Residence& lhs, Residence& rhs) {
return lhs.valuation() < rightResidence.valuation();
}
// This comparator object must be provided if you plan to compare Residence (child) pointers
struct ResidencePointerComparator {
// Notice how we take advantage of operator< by simply dereferencing the Residence pointers
bool operator()(Residence* left, Residence* right) { return (*left) < (*right); }
};
protected:
double price;
};
class Coop: public Residence {
public:
Coop(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (.5*price+20); }
virtual string typeResidence() { return "Coop"; }
};
class Condo: public Residence {
public:
Condo(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (1.2*price); }
virtual string typeResidence() { return "Condo"; }
virtual double taxRate() { return 0.18; }
};
class House: public Residence {
public:
House(double newPrice) : Residence(newPrice) {}
virtual double valuation() { return (price+50); }
virtual string typeResidence() { return "House"; }
};
// This function won't compile if uncommented because Residence is abstract!
// Data structures may or may not call the default constructor
// on its objects when you declare the data structure the first time
// (since vectors allocate data in advance, they definitely would!).
// This is a problem because abstract classes can't declare instances!
// What do you do if you want a data structure full of all kinds of
// Residences? Coops, Condos, and Houses? Enter, pointers to Residences.
// See the next test function for more.
void testResidenceContainerWithObjects()
{
//SortedVector<Residence> sortedVectorOfResidences;
//SortedLinkedList<Residence> sortedLinkedListOfResidences;
}
// This is better because now we can store pointers to Residences and allocate
// the objects they point to (Coops, Condos, and Houses) when we require them.
// HOWEVER, this won't work correctly because the sorting will actually be comparing pointers!
// Technically, pointers are addresses--aka, numbers in hexadecimal. It's therefore
// perfectly legal to compare them! One address can absolutely be "smaller" than another.
// However, this is not the sorting behavior that we want.
void testResidenceContainerWithPointersComparingIncorrectly()
{
SortedVector<Residence*> sortedVectorOfResidences;
SortedLinkedList<Residence*> sortedLinkedListOfResidences;
const unsigned int NUM_RESIDENCES = 10;
for (int i = 0; i < NUM_RESIDENCES; ++i)
{
Residence* residence;
double randomPrice = (rand() % 19 + 1) * 50;
int typeResidence = rand() % 3;
if(typeResidence == 0)
{
residence = new Coop(randomPrice);
}
else if (typeResidence == 1)
{
residence = new Condo(randomPrice);
}
else if (typeResidence == 2)
{
residence = new House(randomPrice);
}
sortedVectorOfResidences.insert(residence);
sortedLinkedListOfResidences.insert(residence);
}
cout << "INCORRECT VERSION (sorted seemingly randomly, but based on pointer address)\n";
cout << "Sorted Vector\n";
for (int i = 0; i < NUM_RESIDENCES; ++i)
{
Residence* currentResidenceFromVector = sortedVectorOfResidences.at(i);
cout << "(" << i+1 << ") ";
currentResidenceFromVector->display();
cout << endl;
}
cout << "Sorted Linked List\n";
for (int i = 0; i < NUM_RESIDENCES; ++i)
{
Residence* currentResidenceFromList = sortedLinkedListOfResidences.at(i);
cout << "(" << i+1 << ") ";
currentResidenceFromList->display();
cout << endl;
}
cout << endl;
}
// In order to work correctly, you must create a "comparator" struct/class that compares
// arguments through its operator(). You supply the pointers to the object as arguments,
// and then compare them after simply dereferencing them! See ResidencePointerComparator.
void testResidenceContainerWithPointersComparingCorrectly()
{
SortedVector<Residence*, Residence::ResidencePointerComparator> sortedVectorOfResidences;
SortedLinkedList<Residence*, Residence::ResidencePointerComparator> sortedLinkedListOfResidences;
const unsigned int NUM_RESIDENCES = 10;
for (int i = 0; i < NUM_RESIDENCES; ++i)
{
Residence* residence;
double randomPrice = (rand() % 19 + 1) * 50;
int typeResidence = rand() % 3;
if(typeResidence == 0)
{
residence = new Coop(randomPrice);
}
else if (typeResidence == 1)
{
residence = new Condo(randomPrice);
}
else if (typeResidence == 2)
{
residence = new House(randomPrice);
}
sortedVectorOfResidences.insert(residence);
sortedLinkedListOfResidences.insert(residence);
}
cout << "\nCORRECT VERSION (sorted based on valuation!)\n";
cout << "Sorted Vector\n";
for (int i = 0; i < NUM_RESIDENCES; ++i)
{
Residence* currentResidenceFromVector = sortedVectorOfResidences.at(i);
cout << "(" << i+1 << ") ";
currentResidenceFromVector->display();
cout << endl;
}
cout << "Sorted Linked List\n";
for (int i = 0; i < NUM_RESIDENCES; ++i)
{
Residence* currentResidenceFromList = sortedLinkedListOfResidences.at(i);
cout << "(" << i+1 << ") ";
currentResidenceFromList->display();
cout << endl;
}
cout << endl;
}
int main() {
srand((unsigned int)time(NULL));
testResidenceContainerWithObjects();
testResidenceContainerWithPointersComparingIncorrectly();
testResidenceContainerWithPointersComparingCorrectly();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment