=Shapes= ==Problem== * You’re making an app that allows kids to create pictures using rectangles and circles of different sizes and colors. * A pictures is now stored as a file with one shape per line * Input format for a rectangle is (unit is pixels) ** color Rectangle height width x y * Input format for a circle is ** color Circle radius x y * All shapes should have an ID (in case two shapes are otherwise identical) ===Problem-solving=== * It helps tremendously to draw the inheritance hierarchy for this problem and place data members and functions in the hierarchy before try to implement the solution ===Part I=== * Your app will ** read in a file of shapes ** create an object for each shape ** print a summary of each shape * For starters, create the base class and create objects of this class * Your printed summary should include the color and unique ID. For example, for a rectangle: ** ID#0, Yellow * For a circle: ** ID#1, Blue ===Part II=== * Next create derived classes and create objects of these classes instead of the base class * To the printed summary, add type and dimensions. For example, for a rectangle: ** Rectangle (25 x 30), ID#0, Yellow * For a circle: ** Circle (radius 12), ID#1, Blue * Let the user select a color to change all of the shapes to before they are printed to the console ===Part III=== * Finally, add functions to find area and edge length for a shape * To the printed summary, add area and edge length. For example, for a rectangle: ** Rectangle (25 x 30), ID#0, Yellow – area:750 – edge:110 * For a circle: ** Circle (radius 12), ID#1, Blue – area:452.39 – edge:75.4 ===Unimplemented Tasks=== ====Scaling==== * What if we wanted to also be able to scale an object’s size? * Let the user scale all objects before printing them to the console ====Moving==== * What if we wanted to include x, y coordinates of the shape’s center in the shape summary? * What if we wanted to be able to move a shape (positive y meaning up and positive x meaning right)? * Allow the user to move all shapes’ positions by a certain amount before printing them to the console ==Solution== ===Source.cpp=== /* Test case classes (you should provide specific test cases) Test file should include both Circle and a Rectangle Test file should include shapes with dimension values of 0 Test file should include shapes of many different colors and sizes */ #include #include #include #include #include #include "Circle.h" #include "Rectangle.h" #include "Shape.h" using namespace std; void create_objects_from_file(string filename, vector & shapes); int main() { vector shapes; // 1. READ IN A FILE AND CREATE AN OBJECT FOR EACH SHAPE cout << "Filename:" << endl; string filename; getline(cin, filename); // static helper function create_objects_from_file(filename, shapes); // Let the user select a color to change all of the shapes to before they are printed to the console string user_fav_color; cout << "Fav color: "; getline(cin, user_fav_color); // 2. PRINT A SUMMARY OF THE SHAPES (to_string() is fine) for (int i = 0; i < shapes.size(); i++) { // All derived classes have access to the base class set_color() function shapes[i]->set_color(user_fav_color); // Each derived class implements and executes these differently. HERE IS POLYMORPHISM cout << shapes[i]->to_string() << endl; cout << "\tAREA:" << shapes[i]->area() << endl; cout << "\tEDGE:" << shapes[i]->edge_length() << endl; } system("pause"); return 0; } /* Function to load contents of a file and create shape objects depending on file contents @param filename name of file to load shapes from @param shapes vector into which to load new shape objects */ void create_objects_from_file(string filename, vector & shapes) { ifstream in_file; in_file.open(filename); // Note: we don't test if the filename is valid or not string line; // loop continues as long as another line can be read while (getline(in_file, line)) { // cout << "LINE: " << line << endl; // use an istringstream when trying to read different data types from a string istringstream strm(line); string color; string shape_type; strm >> color; strm >> shape_type; // This is a pointer to the shape we are going to create for the given line Shape * new_shape = NULL; if (shape_type == "Rectangle") { int width, height; strm >> width >> height; // The substitution principle allows us to put an object of a derived class // where an object of the base class is expected new_shape = new Rectangle(color, width, height); } else if (shape_type == "Circle") { int radius; strm >> radius; // The substitution principle allows us to put an object of a derived class // where an object of the base class is expected new_shape = new Circle(color, radius); } // cout << "shape created with color: " << new_shape->get_color() << endl; // this is NULL if the shape_type was neither Rectangle nor Circle if (new_shape != NULL) { shapes.push_back(new_shape); } } } ===Shape.h=== #include using namespace std; #pragma once class Shape { public: // CONSTRUCTOR /* Default constructor for a Shape object */ Shape(); // OTHER MEMBER FUNCTIONS /* @return color of *this* Shape object */ string get_color() const; /* @param new_color color to be set as *this* Shape object's color */ void set_color(string new_color); /* @return the unique id for this Shape */ int get_id() const; /* Function to compute shape's area. Note that this can't be computed for a generic shape hence why it is a pure virtual function @return the area of *this* Shape object */ // a Pure Virtual Function must, by definition, be overwritten by any derived class virtual double area() const = 0; /* Function to compute shape's perimeter edge length. Note that this can't be computed for a generic shape hence why it is a pure virtual function @returns the perimeter of *this* Shape object */ // a Pure Virtual Function must, by definition, be overwritten by any derived class virtual double edge_length() const = 0; /* @return a string representation of *this* Shape object */ virtual string to_string() const; private: // DATA MEMBERS string color; int id; // static variables exist once for all instances of the class static int next_id; }; ===Shape.cpp=== Note that even though Shape is an abstract class (by virtue of having a pure virtual function), there is still an implementation file. #include #include "Shape.h" // initialize our static variable int Shape::next_id = 0; Shape::Shape() { color = ""; id = next_id; // static variable at work next_id++; } string Shape::get_color() const { return color; } void Shape::set_color(string new_color) { color = new_color; } int Shape::get_id() const { return id; } string Shape::to_string() const { // use an ostringstream when creating a string from different data types ostringstream strm; strm << "ID# " << id << ", " << color; return strm.str(); } ===Circle.h=== #pragma once // to BE a Shape, we must know what a Shape is #include "Shape.h" // Here is where we define that a Circle IS-A Shape and inherits all of its members and functions class Circle : public Shape { public: // CONSTRUCTOR /* Constructor to construct a new Circle object @param _color color of new Circle object @param _radius radius of the new Circle object */ Circle(string _color, int _radius); // MEMBER FUNCTION /* @return a string representation of *this* Circle object */ virtual string to_string() const; /* @return area of *this* Circle object */ virtual double area() const; /* @return perimeter edge length of *this* Circle object */ virtual double edge_length() const; private: // DATA MEMBERS int radius; // Note that this inherits all other data members and member functions that it needs from the base class }; ===Circle.cpp=== #include #include "Circle.h" Circle::Circle(string _color, int _radius) :Shape(), radius(_radius) // Everything after ':' is the initializer list // The Shape() constructor is called by default but we include it for emphasis { set_color(_color); } string Circle::to_string() const { // use an ostringstream when creating a string from different data types ostringstream strm; strm << "Circle (radius " << radius << "), " << Shape::to_string(); return strm.str(); } double Circle::area() const { return 3.14159 * radius * radius; } double Circle::edge_length() const { return 2 * 3.14159 * radius; } ===Rectangle.h=== #pragma once // to BE a Shape, we must know what a Shape is #include "Shape.h" // Here is where we define that a Rectangle IS-A Shape and inherits all of its members and functions class Rectangle : public Shape { public: // CONSTRUCTOR /* Constructor to construct a new Rectangle object @param _color color of new Rectangle object @param _radius radius of the new Rectangle object */ Rectangle(string _color, int _width, int _height); // MEMBER FUNCTION /* @return a string representation of *this* Rectangle object */ virtual string to_string() const; /* @return area of *this* Rectangle object */ virtual double area() const; /* @return perimeter edge length of *this* Rectangle object */ virtual double edge_length() const; private: int width; int height; // Note that this inherits all other data members and member functions that it needs from the base class }; ===Rectangle.cpp=== #include #include "Rectangle.h" Rectangle::Rectangle(string _color, int _width, int _height) :Shape(), width(_width), height(_height) // Everything after ':' is the initializer list // The Shape() constructor is called by default but we include it for emphasis { set_color(_color); } string Rectangle::to_string() const { // use an ostringstream when creating a string from different data types ostringstream strm; strm << "Rectangle (" << width << " x " << height << "), " << Shape::to_string(); return strm.str(); } double Rectangle::area() const { return width * height; } double Rectangle::edge_length() const { return 2 * width + 2 * height; } ===shapes.txt=== Here is a possible input file
Red Circle 38 12 22
Blue Circle 18 56 79
Green Rectangle 22 42 190 76
Yellow Circle 78 78 190
Black Circle 1 78 78
Supercalifragilisticexpialidocious this is the only error we test
Grey Circle 3 78 78
Black Circle 5 78 78
Green Rectangle 10 10 100 100
Pink Circle 10 50 50