//------------------------------------------------------------------------- // // rabbit4.cpp // // Written by: Simon Parsons // Last modified: 25th April 2010 // A revised version of the ecosystem with an example of a virtual function. // The virtual function beEaten allows us to get different "eaten" behavior // from a polymorphic function. // Include the necessary header files #include #include #include using namespace std; //------------------------------------------------------------------------- // enum direction {north, east, south, west}; const int SIZE = 5; const int CARROTS = 3; //------------------------------------------------------------------------- // // point // // Our old favorite // class point{ private: // The only attributes of the point are x and y coordinates. int x, y; public: // The interface methods get the x and y coordinates of the point, // set the coordinates, and print their values. int getX() const; int getY() const; void set(int x, int y); void print() const; }; int point::getX() const{ return x; } int point::getY() const{ return y; } void point::set(int x, int y){ this->x = x; this->y = y; } void point::print() const{ cout << "(" << x << ", " << y << ")" << endl; } // // End of point //------------------------------------------------------------------------- // // living // // A living thing has a location. class living { protected: // Location and eaten are protected to allow them to be modified by // derived classes. point location; bool eaten; public: // Most of the interface methods for living are just the same as for a // point, and just call the ones for point. int getX() const; int getY() const; void set(int x, int y); void print() const; // We also have a constructor that ensures thingss are not created // already eaten and a virtual method for being eaten. living(){eaten = false;}; virtual void beEaten(){cout << "I live, therefore I can be eaten" << endl;} }; int living::getX() const{ return location.getX(); } int living::getY() const{ return location.getY(); } void living::set(int x, int y){ location.set(x, y); } void living::print() const{ location.print(); } // // End of living //------------------------------------------------------------------------- // // plant // // A plant is a kind of thing that doesn't like to beEaten. class plant : public living { public: void beEaten(); }; void plant::beEaten(){ cout << "Pah!" << endl; eaten = true; } // // End of plant //------------------------------------------------------------------------- // // carrot // // A carrot is a kind of plant which responds differently to being eaten. class carrot : public plant { public: void beEaten(); }; void carrot::beEaten(){ cout << "Oh no, not again!" << endl; eaten = true; } // // End of plant //------------------------------------------------------------------------- // // animal // // An animal is a thing that can move, eat, be eaten, and be // hungry. It contains a data member consumed that counts how many // things it has eaten. class animal : public living { protected: int consumed; public: animal(){consumed = 0;}; void move(); void move(direction d); void eat(); bool hungry(); void beEaten(); }; // Pick a random direction to move in, and then move one unit in that direction void animal::move(){ direction d; d = static_cast(rand() % 4); move(d); } // When the animal moves, the world "wraps around", so, for example, // if the animal is at the east end of the world and moves east, it // appears at the far west end. // // The overloaded function move provides this abilty. void animal::move(direction d){ int x = location.getX(); int y = location.getY(); // Find a new x and y coordinate if (d == north){ y = (y + 1) % SIZE; } if (d == south){ y = (y - 1); if (y < 0){ y = SIZE; } } if (d == east){ x = (x + 1) % SIZE; } if (d == west) { x = (x - 1) % SIZE; if (x < 0){ x = SIZE; } } // Set the location of the animal to those coordinates location.set(x, y); }; void animal::eat(){ cout << "Yum!" << endl; consumed++; } // A animal is hungry unless it has eaten something bool animal::hungry(){ if (consumed == 0){ return true; } else { return false; } } // beEaten is a function that we don't expect to use, but without it // animal is an abstract class, and we can't write functions that take // arguments of type animal. void animal::beEaten(){ cout << "WTF?" << endl; } // // End of animal //------------------------------------------------------------------------- // // rabbit // // A rabbit is a kind of animal with its own way to beEaten. class rabbit : public animal { public: void beEaten(); }; void rabbit::beEaten(){ cout << "Drat that fox!" << endl; eaten = true; } // // End of rabbit //------------------------------------------------------------------------- // // fox // // A fox is a kind of animal with its own way of moving. // // Note that even though foxes don't get eaten in this model, we need // to have a function definition for beEaten, or else fox is an // abstract class. class fox : public animal { public: void move(); void move(direction d); }; void fox::move(){ direction d; d = static_cast(rand() % 4); move(d); } void fox::move(direction d){ int x = location.getX(); int y = location.getY(); // Find a new x and y coordinate if (d == north){ y = (y + 2) % SIZE; } if (d == south){ y = (y - 2); if (y < 0){ y = SIZE; } } if (d == east){ x = (x + 2) % SIZE; } if (d == west) { x = (x - 2) % SIZE; if (x < 0){ x = SIZE; } } location.set(x, y); } // // End of fox // howDoYouDie // // There is no world object in this version of the code. But we do have a // polymorphic function. Since all the objects extend living, we can write // a function to which we can pass every object we create. // The function takes a pointer to an object as its argument, and calls the // relevant beEaten function. void howDoYouDie(living *ptr){ ptr->beEaten(); } //------------------------------------------------------------------------- // In main() we don't do much this time, we just show how beEaten behaves. int main( void ) { // Since everything is a subclass of living, we can use a pointer to living // to point to each object. living l; living *lptr = &l; cout << "A living says: "; howDoYouDie(lptr); // When we pass the pbject, by pointer, to howDoYouDie(), the fact that // beEaten() is virtual means the most specific version of beEaten() gets // called. plant p; lptr = &p; cout << "A plant says: "; howDoYouDie(lptr); carrot c; lptr = &c; cout << "A carrot says: "; howDoYouDie(lptr); animal a; lptr = &a; cout << "An animal says: "; howDoYouDie(lptr); rabbit r; lptr = &r; cout << "A rabbit says: "; howDoYouDie(lptr); // Since fox doesn't have a beEaten() definition of its own, the one from // animal gets called. fox f; lptr = &f; cout << "A fox says: "; howDoYouDie(lptr); cout << "because it doesn't over-ride beEaten" << endl; return 0; } // end of main()