Created
September 14, 2017 16:02
-
-
Save simonayzman/30a6b4ad50572f6fa4dbbcb00e600584 to your computer and use it in GitHub Desktop.
Applying Polymorphism to Data in Data Structures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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