Sei sulla pagina 1di 41

Chapter 15

INHERITANCE 1. Solutions to and Remarks on Selected Programming Problems 1. Derive class Administrator from class SalariedEmployee Please note an error in the statement of Programming Project 1: In the requirement for a member function called "print" which the problem says "... output the programmer's data to the screen." This should read "... output the administrator's data to the screen." Remarks: Ideally the member functions in the base class Employee that are going to be declared in derived classes should be declared "pure virtual" where the body {...} is replaced by = 0;. This results in the compiler forcing any directly derived function to define a member with this signature, which, presumably is better suited than any base class member function will be. Print check may well be different for each derived class. The member function void give_raise(double) could be different as well. Regarding the function that reads the administrator's data, note that a new administrator's data will be entered in response to the base class Employee and derive class SalariedEmployee inquiries, then the administrator class function will make inquiry. My design and implementation follow: Title: Derive class Administrator Write a program using classes from Display 15.1 - 15.4 Define class Administrator which derives publicly from class SalariedEmployee. Explain why there is a need to change private: to protected: Answer: To allow the member functions inherited class member to see these data members. Additional protected members required are:

1 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

string title administrator's title (CEO, Director etc.) string responsibility (production, accounting, personnel) string supervisor (name of immediate supervisor)

double annual_salary (if not already added in the self test exercise) Additional public members required are: A function to change name of immediate supervisor void change_supervisor(string new_super); A function for reading all of a new administrator's data from the keyboard. Note that the data inherited from class Employee will already be fetched by required base class constructors. class constructors. void get_admin_data(); function to output administrator's data to the screen void print(); a function to print a check with appropriate notations on the check void print_check() //file: ch15p1.h //This is the INTERFACE for class Administrator which //inherits from class Employee and class SalariedEmployee #ifndef ADMINISTRATOR_H

2 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

#define ADMINISTRATOR_H #include "employee.h" //from Display 15.1 namespace employeessavitch { class Administrator : public SalariedEmployee { public: Administrator(); Administrator(string aTitle, string aResponsibilty, string aSupervisor); // change name of immediate supervisor void change_supervisor(string new_super); // Read a new administrator's data from the keyboard. void get_admin_data(); //outputs administrator's data to the screen void print(); // prints a check with appropriate notations on the check void print_check(); void give_raise(double amount); protected: string title; // administrator's title //(CEO, Director, Vice President) string responsibility;//production, accounting, personnel string supervisor ; //name of immediate supervisor long double annual_salary; // if not added in the self

3 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

//test exercise }; } #endif //File: ch15p1.cc //Chapter 15 Programming Project 1 //Implementation for the Administrator derived class #include "employee.h" //display 15.2 #include "ch15p1.h" #include <iostream> namespace employeesavitch { void Administrator::give_raise(double amount) { annual_salary += amount; } Administrator:: Administrator() : SalariedEmployee() { using namespace std; cout << "\nAdministrative Employee data " <<"is being entered:\n\n"; get_admin_data(); } Administrator:: //Previous header file

4 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

Administrator(string aTitle, string aResponsibilty, string aSupervisor ) : SalariedEmployee(), title( aTitle ), responsibility(aResponsibilty ), supervisor( aSupervisor ) { //deliberately empty } // change name of immediate supervisor void { supervisor = new_super; } //Reading all of a new administrator's data from the //keyboard. void { using namespace std; cout << "Enter title, followed by return:\n"; getline(cin, title); cout << "\nEnter area of responsibility " << "followed by return:\n"; getline(cin, responsibility); Administrator::get_admin_data() Administrator::change_supervisor(string new_super)

5 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

cout << "\nEnter supervisor's name," << " followed by return:\n"; getline(cin, supervisor); cout << "\nEnter salary followed by return:\n"; cin >> annual_salary; } //outputs administrator's data to the screen void Administrator::print() { using namespace std; cout << "\n\nprinting data \n"; cout << "Name: " << name << endl; cout << "Annual Salary: $" << annual_salary << endl; cout << "Social Security Number: " << ssn << endl; cout << "Title: "; cout << title << endl; cout << "Responsibility:"; cout << responsibility << endl; cout << "supervisor: "; cout << supervisor << endl; cout.setf(ios::showpoint); cout.setf(ios::fixed); cout.precision(2); cout << "Annual Salary: ";

6 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

cout << annual_salary << endl; } // prints a check with appropriate notations on the check void Administrator::print_check() { using namespace std; long double monthly_pay = annual_salary/12; cout << "\n__________________________________________________\n"; cout << "Pay to the order of " << name << endl; cout.setf(ios::showpoint); cout.setf(ios::fixed); cout.precision(2); cout << "The sum of cout << "_________________________________________________\n"; cout << "Check Stub NOT NEGOTIABLE \n"; cout << "Employee Number: " << ssn << endl; cout << "Administrative Employee. \n"; cout << "_________________________________________________\n"; } } // end namespace employeesavitch 2. Add Temporary, Administrative, Permenant etc classifications to 15.1 No solution is provided for this problem. 3. Derive class Doctor from SalariedEmployee $" << monthly_pay << " Dollars\n";

7 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

No solution is provided for this problem. 4. Create a base class vehicle No solution is provided for this problem. 5. Define classes Patient and Billing No solution is provided for this problem. 6. No Solution Provided. 7. Implement a graphics System Implement a graphics System that has classes for various figures: rectangles, squares, triangles, circles, etc. A rectangle has data members height, width, and center point. A square has center point and an edge. A circle has data members center and radius. The several specific shape classes are derived from a common class, Figure. Implement only Rectangle and Triangle classes. Derive them from Figure. Each class has stubs for erase and draw. The class Figure has member function center that calls erase and draw. The functions erase and draw are only stubs, consequently center does little but call erase and draw. Add a message to center announcing that it is being called. There are 3 parts: a) Write the class definitions using no virtual functions. Compile and test. b) Make the base class member functions virtual. Compile and test. c) Explain the differences. Use the following main function: #include <iostream> #include "figure.h" #include "rectangle.h" #include "triangle.h" using std::cout;

8 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

int main() { Triangle tri; tri.draw(); cout << "\nDerived class Triangle object calling" << " center().\n"; tri.center(); Rectangle rect; rect.draw(); cout << "\nDerived class Rectangle object calling" << " center().\n"; rect.center(); return 0; } Solution: I have used no member variables, as they serve no function in this part of the exercise. //figure.h #include <iostream> using namespace std; #ifndef PROTECT_FIGURE_H #define PROTECT_FIGURE_H class Figure { public: //virtual //Uncomment to make erase() a virtual function

9 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

void erase(); //virtual //Uncomment to make draw() a virtual function void draw(); void center() { cout << "member function center called " << endl; erase(); draw(); } }; #endif //File: figure.cpp #include "figure.h" void Figure::erase() { cout << "Figure::erase()" << endl; } void Figure::draw() { cout << "Figure::draw()" << endl; } // end of file figure.cpp //rectangle.h #include <iostream> #include "figure.h"

10 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

using namespace std; class Rectangle : public Figure { public: void erase(); void draw(); }; void Rectangle::erase() { cout << "Rectangle::erase() " << endl; } void Rectangle::draw() { cout << "Rectangle::draw() " << endl; }

//triangle.h #include <iostream> #include "figure.h" using namespace std; class Triangle : public Figure { public:

11 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

void draw(); void erase(); }; void Triangle::draw() { cout << "Triangle::draw() " << endl; } void Triangle::erase() { cout << "Triangle::erase()" << endl; }

//ch13p1App.cpp #include <iostream> #include "figure.h" #include "rectangle.h" #include "triangle.h" using std::cout; int main() { Triangle tri; tri.draw(); cout << "\nIn main, Derived class Triangle object calling" << " center().\n";

12 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

tri.center(); Rectangle rect; rect.draw(); cout << "\nIn main, Derived class Rectangle object calling" << " center().\n"; rect.center(); return 0; } A trial run follows: With NON virtual draw() and erase() Triangle::draw() In main, Derived class Triangle object calling center(). member function center called Figure::erase() Figure::draw() Rectangle::draw() In main, Derived class Rectangle object calling center(). member function center called Figure::erase() Figure::draw() With virtual draw() and erase(): Triangle::draw() In main, Derived class Triangle object calling center(). member function center called Triangle::erase()

13 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

Triangle::draw() Rectangle::draw() Explanation of difference The differences are due solely to the presence or absence of the keyword virtual in the base class figure. 8. Flesh out Problem 7 Give new definitions and member functions for Figure::center, Figure::draw, Figure::erase, Triangle::draw, Triangle::erase, Rectangle::draw, Rectangle::erase, so that the draw functions actually draw figures on the screen using asterisks (*). For the erase function, clear the screen. No solution to this part is provided. 9. No Solution Provided. 10.
// **************************************************************** // // Ch15Proj10.cpp // // This program uses inheritance to simulate retrieving the contents // of a RFID shipping container and a manual shipping container. // **************************************************************** #include #include #include #include <iostream> <vector> <string> <sstream>

using namespace std; // ************************* Begin ShippingContainer Class class ShippingContainer

14 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

{ public: ShippingContainer(); ShippingContainer(int newID); int getID(); void setID(int newID); virtual string getManifest(); private: int id; }; // ====================== // ShippingContainer::ShippingContainer // Constructors // ====================== ShippingContainer::ShippingContainer() { id = -1; } ShippingContainer::ShippingContainer(int newID) { id = newID; } // ====================== // ShippingContainer::getID // Returns the ID number // ====================== int ShippingContainer::getID() { return id; } // ====================== // ShippingContainer::setID // Sets the ID number // ====================== void ShippingContainer::setID(int newID) { id = newID; } // ====================== // ShippingContainer::getManifest // Returns empty contents // ====================== string ShippingContainer::getManifest() { return ""; } // ************************* Begin ManualShippingContainer Class

15 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

class ManualShippingContainer : public ShippingContainer { public: ManualShippingContainer(int newID); void setManifest(string s); virtual string getManifest(); private: string contents; }; // ====================== // ManualShippingContainer::ManualShippingContainer // Constructor // ====================== ManualShippingContainer::ManualShippingContainer(int newID) : ShippingContainer(newID) { } // ====================== // ManualShippingContainer::setManifest // Sets the manifest for this container // ====================== void ManualShippingContainer::setManifest(string s) { contents = s; } // ====================== // ManualShippingContainer::getManifest // Returns the manifest for this container // ====================== string ManualShippingContainer::getManifest() { return contents; } // ************************* Begin RFIDShippingContainer Class class RFIDShippingContainer : public ShippingContainer { public: RFIDShippingContainer(int newID); void add(string s); virtual string getManifest(); private: vector<string> contents; vector<int> count; int search(string s); }; // ====================== // RFIDShippingContainer::RFIDShippingContainer

16 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

// Constructor // ====================== RFIDShippingContainer::RFIDShippingContainer(int newID) : ShippingContainer(newID) { } // ====================== // RFIDShippingContainer::search // Search the vector for duplicates and if found // returns the index, or -1 if not found // ====================== int RFIDShippingContainer::search(string s) { int i; for (i=0; i<contents.size(); i++) { if (contents[i] == s) return i; } return -1; } // ====================== // RFIDShippingContainer::add // Adds a new string to the manifest vector. // ====================== void RFIDShippingContainer::add(string s) { int i; i = search(s); if (i == -1) { contents.push_back(s); // Add item count.push_back(1); // One occurrence of this item } else { count[i]++; // Add to occurrences of this item } } // ====================== // RFIDShippingContainer::getManifest // Returns the manifest for this container // by scanning through the vector // ====================== string RFIDShippingContainer::getManifest() { string s = ""; int i; for (i=0; i< contents.size(); i++) { // Convert number to string using stringstream stringstream converter;

17 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} return s;

converter << count[i]; s+= converter.str() + " " + contents[i] + ". ";

// ====================== // main function // ====================== int main() { // Variable declarations with test data ShippingContainer* containers[6]; ManualShippingContainer m1(100),m2(200),m3(300); RFIDShippingContainer r1(400),r2(500),r3(600); m1.setManifest("1000 diapers"); m2.setManifest("1000 candy bars. m3.setManifest("500 books."); r1.add("apples"); r1.add("apples"); r1.add("cookies"); r2.add("pineapple"); r2.add("pears"); r2.add("pineapple"); r2.add("pears"); r3.add("chocolate bars"); r3.add("chocolate bars"); r3.add("chocolate bars"); // Store containers in the array containers[0] = &m1; containers[1] = &m2; containers[2] = &m3; containers[3] = &r1; containers[4] = &r2; containers[5] = &r3; // Loop to output contents of the containers for (int i=0; i<6; i++) { cout << "Container " << containers[i]->getID() << " contains " << containers[i]->getManifest() << endl; } return 0; } 500 toilet paper.");

11.
// **************************************************************** // // Ch15Proj11.cpp

18 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

// // This program simulates a 2D world with predators and prey. // The predators (doodlebugs) and prey (ants) inherit from the // Organism class that keeps track of basic information about each // critter (time ticks since last bred, position in the world). // // The 2D world is implemented as a separate class, World, // that contains a 2D array of pointers to type Organism. // // Normally the classes would be defined in separate files, but // they are all included here for ease of use with CodeMate. // **************************************************************** #include #include #include #include #include <iostream> <string> <vector> <cstdlib> <time.h>

using namespace std; const const const const const const const const int int int int int int int int WORLDSIZE = 20; INITIALANTS = 100; INITIALBUGS = 5; DOODLEBUG = 1; ANT = 2; ANTBREED = 3; DOODLEBREED = 8; DOODLESTARVE = 3;

// Forward declaration of Organism classes so we can reference it // in the World class class Organism; class Doodlebug; class Ant; // // // // // // // // ========================================== The World class stores data about the world by creating a WORLDSIZE by WORLDSIZE array of type Organism. NULL indicates an empty spot, otherwise a valid object indicates an ant or doodlebug. To determine which, invoke the virtual function getType of Organism that should return ANT if the class is of type ant, and DOODLEBUG otherwise. ==========================================

class World { friend class Organism; // Allow Organism to access grid friend class Doodlebug; // Allow Organism to access grid friend class Ant; // Allow Organism to access grid public: World(); ~World(); Organism* getAt(int x, int y);

19 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

void setAt(int x, int y, Organism *org); void Display(); void SimulateOneStep(); private: Organism* grid[WORLDSIZE][WORLDSIZE]; }; // ========================================== // Definition for the Organism base class. // Each organism has a reference back to // the World object so it can move itself // about in the world. // ========================================== class Organism { friend class World; // Allow world to affect organism public: Organism(); Organism(World *world, int x, int y); ~Organism(); virtual void breed() = 0; // Whether or not to breed virtual void move() = 0; // Rules to move the critter virtual int getType() =0; // Return if ant or doodlebug virtual bool starve() = 0; // Determine if organism starves protected: int x,y; // Position in the world bool moved; // Bool to indicate if moved this turn int breedTicks; // Number of ticks since breeding World *world; }; // ====================== // World constructor, destructor // These classes initialize the array and // releases any classes created when destroyed. // ====================== World::World() { // Initialize world to empty spaces int i,j; for (i=0; i<WORLDSIZE; i++) { for (j=0; j<WORLDSIZE; j++) { grid[i][j]=NULL; } } } World::~World() { // Release any allocated memory int i,j;

20 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

for (i=0; i<WORLDSIZE; i++) { for (j=0; j<WORLDSIZE; j++) { if (grid[i][j]!=NULL) delete (grid[i][j]); } }

// ====================== // getAt // Returns the entry stored in the grid array at x,y // ====================== Organism* World::getAt(int x, int y) { if ((x>=0) && (x<WORLDSIZE) && (y>=0) && (y<WORLDSIZE)) return grid[x][y]; return NULL; } // ====================== // setAt // Sets the entry at x,y to the // value passed in. Assumes that // someone else is keeping track of // references in case we overwrite something // that is not NULL (so we don't have a memory leak) // ====================== void World::setAt(int x, int y, Organism *org) { if ((x>=0) && (x<WORLDSIZE) && (y>=0) && (y<WORLDSIZE)) { grid[x][y] = org; } } // ====================== // Display // Displays the world in ASCII. Uses o for ant, X for doodlebug. // ====================== void World::Display() { int i,j; cout << endl << endl; for (j=0; j<WORLDSIZE; j++) { for (i=0; i<WORLDSIZE; i++) { if (grid[i][j]==NULL) cout << "."; else if (grid[i][j]->getType()==ANT) cout << "o"; else cout << "X"; }

21 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

cout << endl;

// ====================== // SimulateOneStep // This is the main routine that simulates one turn in the world. // First, a flag for each organism is used to indicate if it has moved. // This is because we iterate through the grid starting from the top // looking for an organism to move . If one moves down, we don't want // to move it again when we reach it. // First move doodlebugs, then ants, and if they are still alive then // we breed them. // ====================== void World::SimulateOneStep() { int i,j; // First reset all organisms to not moved for (i=0; i<WORLDSIZE; i++) for (j=0; j<WORLDSIZE; j++) { if (grid[i][j]!=NULL) grid[i][j]->moved = false; } // Loop through cells in order and move if it's a Doodlebug for (i=0; i<WORLDSIZE; i++) for (j=0; j<WORLDSIZE; j++) { if ((grid[i][j]!=NULL) && (grid[i][j]>getType()==DOODLEBUG)) { if (grid[i][j]->moved == false) { grid[i][j]->moved = true; // Mark as moved grid[i][j]->move(); } } } // Loop through cells in order and move if it's an Ant for (i=0; i<WORLDSIZE; i++) for (j=0; j<WORLDSIZE; j++) { if ((grid[i][j]!=NULL) && (grid[i][j]->getType()==ANT)) { if (grid[i][j]->moved == false) { grid[i][j]->moved = true; // Mark as moved grid[i][j]->move(); } } } // Loop through cells in order and check if we should breed for (i=0; i<WORLDSIZE; i++) for (j=0; j<WORLDSIZE; j++) {

22 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} // Loop through cells in order and check if we should breed for (i=0; i<WORLDSIZE; i++) for (j=0; j<WORLDSIZE; j++) { // Only breed organisms that have moved, since // breeding places new organisms on the map we // don't want to try and breed those if ((grid[i][j]!=NULL) && (grid[i][j]->moved==true)) { grid[i][j]->breed(); } }

// Kill off any doodlebugs that haven't eaten recently if ((grid[i][j]!=NULL) && (grid[i][j]->getType()==DOODLEBUG)) { if (grid[i][j]->starve()) { delete (grid[i][j]); grid[i][j] = NULL; } }

// ====================== // Organism Constructor // Sets a reference back to the World object. // ====================== Organism::Organism() { world = NULL; moved = false; breedTicks = 0; x=0; y=0; } Organism::Organism(World *wrld, int x, int y) { this->world = wrld; moved = false; breedTicks = 0; this->x=x; this->y=y; wrld->setAt(x,y,this); } // ====================== // Organism destructor // No need to delete the world reference, // it will be destroyed elsewhere. // ====================== Organism::~Organism() {

23 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} // Start with the Ant class class Ant : public Organism { friend class World; public: Ant(); Ant(World *world, int x, int y); void breed(); // Must define void move(); // Must define int getType(); // Must define bool starve() // Return false, { return false; } }; // ====================== // Ant constructor // ====================== Ant::Ant() : Organism() { } Ant::Ant(World *world, int x, int y) : Organism(world,x,y) { } // ====================== // Ant Move // Look for an empty cell up, right, left, or down and // try to move there. // ====================== void Ant::move() { // Pick random direction to move int dir = rand() % 4; // Try to move up, if not at an edge and empty spot if (dir==0) { if ((y>0) && (world->getAt(x,y-1)==NULL)) { world->setAt(x,y-1,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); y--; } } // Try to move down else if (dir==1) { if ((y<WORLDSIZE-1) && (world->getAt(x,y+1)==NULL)) { world->setAt(x,y+1,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); // Set current spot to empty y++;

this since virtual this since virtual this since virtual ant never starves

24 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} } // Try to move left else if (dir==2) { if ((x>0) && (world->getAt(x-1,y)==NULL)) { world->setAt(x-1,y,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); // Set current spot to empty x--; } } // Try to move right else { if ((x<WORLDSIZE-1) && (world->getAt(x+1,y)==NULL)) { world->setAt(x+1,y,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); // Set current spot to empty x++; } }

// ====================== // Ant getType // This virtual function is used so we can determine // what type of organism we are dealing with. // ====================== int Ant::getType() { return ANT; } // ====================== // Ant breed // Increment the tick count for breeding. // If it equals our threshold, then clone this ant either // above, right, left, or below the current one. // ====================== void Ant::breed() { breedTicks++; if (breedTicks == ANTBREED) { breedTicks = 0; // Try to make a new ant either above, left, right, or down if ((y>0) && (world->getAt(x,y-1)==NULL)) { Ant *newAnt = new Ant(world, x, y-1); } else if ((y<WORLDSIZE-1) && (world->getAt(x,y+1)==NULL)) { Ant *newAnt = new Ant(world, x, y+1);

25 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} else if ((x>0) && (world->getAt(x-1,y)==NULL)) { Ant *newAnt = new Ant(world, x-1, y); } else if ((x<WORLDSIZE-1) && (world->getAt(x+1,y)==NULL)) { Ant *newAnt = new Ant(world, x+1, y); }

// ***************************************************** // Now define Doodlebug Class // ***************************************************** class Doodlebug : public Organism { friend class World; public: Doodlebug(); Doodlebug(World *world, int x, int y); void breed(); // Must define this since virtual void move(); // Must define this since virtual int getType(); // Must define this since virtual bool starve(); // Check if a doodlebug starves to death private: int starveTicks; // Number of moves before starving }; // ====================== // Doodlebug constructor // ====================== Doodlebug::Doodlebug() : Organism() { starveTicks = 0; } Doodlebug::Doodlebug(World *world, int x, int y) : Organism(world,x,y) { starveTicks = 0; } // ====================== // Doodlebug move // Look up, down, left or right for a bug. If one is found, move there // and eat it, resetting the starveTicks counter. // ====================== void Doodlebug::move() { // Look in each direction and if a bug is found move there // and eat it. if ((y>0) && (world->getAt(x,y-1)!=NULL) && (world->getAt(x,y-1)->getType() == ANT))

26 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} else if ((y<WORLDSIZE-1) && (world->getAt(x,y+1)!=NULL) && (world->getAt(x,y+1)->getType() == ANT)) { delete (world->grid[x][y+1]); // Kill ant world->grid[x][y+1] = this; // Move to spot world->setAt(x,y,NULL); starveTicks =0 ; // Reset hunger y++; return; } else if ((x>0) && (world->getAt(x-1,y)!=NULL) && (world->getAt(x-1,y)->getType() == ANT)) { delete (world->grid[x-1][y]); // Kill ant world->grid[x-1][y] = this; // Move to spot world->setAt(x,y,NULL); starveTicks =0 ; // Reset hunger x--; return; } else if ((x<WORLDSIZE-1) && (world->getAt(x+1,y)!=NULL) && (world->getAt(x+1,y)->getType() == ANT)) { delete (world->grid[x+1][y]); // Kill ant world->grid[x+1][y] = this; // Move to spot world->setAt(x,y,NULL); starveTicks =0 ; // Reset hunger x++; return; } // If we got here, then we didn't find food. Move // to a random spot if we can find one. int dir = rand() % 4; // Try to move up, if not at an edge and empty spot if (dir==0) { if ((y>0) && (world->getAt(x,y-1)==NULL)) { world->setAt(x,y-1,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); y--; } } // Try to move down

delete (world->grid[x][y-1]); // Kill ant world->grid[x][y-1] = this; // Move to spot world->setAt(x,y,NULL); starveTicks =0 ; // Reset hunger y--; return;

27 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

else if (dir==1) { if ((y<WORLDSIZE-1) && (world->getAt(x,y+1)==NULL)) { world->setAt(x,y+1,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); // Set current spot to empty y++; } } // Try to move left else if (dir==2) { if ((x>0) && (world->getAt(x-1,y)==NULL)) { world->setAt(x-1,y,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); // Set current spot to empty x--; } } // Try to move right else { if ((x<WORLDSIZE-1) && (world->getAt(x+1,y)==NULL)) { world->setAt(x+1,y,world->getAt(x,y)); // Move to new spot world->setAt(x,y,NULL); // Set current spot to empty x++; } } starveTicks++; // Increment starve tick since we didn't // eat on this turn

// ====================== // Doodlebug getType // This virtual function is used so we can determine // what type of organism we are dealing with. // ====================== int Doodlebug::getType() { return DOODLEBUG; } // ====================== // Doodlebug breed // Creates a new doodlebug adjacent to the current cell // if the breedTicks meets the threshold. // ====================== void Doodlebug::breed() { breedTicks++; if (breedTicks == DOODLEBREED) { breedTicks = 0;

28 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

// Try to make a new ant either above, left, right, or down if ((y>0) && (world->getAt(x,y-1)==NULL)) { Doodlebug *newDoodle = new Doodlebug(world, x, y-1); } else if ((y<WORLDSIZE-1) && (world->getAt(x,y+1)==NULL)) { Doodlebug *newDoodle = new Doodlebug(world, x, y+1); } else if ((x>0) && (world->getAt(x-1,y)==NULL)) { Doodlebug *newDoodle = new Doodlebug(world, x-1, y); } else if ((x<WORLDSIZE-1) && (world->getAt(x+1,y)==NULL)) { Doodlebug *newDoodle = new Doodlebug(world, x+1, y); }

// ====================== // Doodlebug starve // Returns true or false if a doodlebug should die off // because it hasn't eaten enough food. // ====================== bool Doodlebug::starve() { // Starve if no food eaten in last DOODLESTARVE time ticks if (starveTicks > DOODLESTARVE) { return true; } else { return false; } }

// ====================== // main function // ====================== int main() { string s; srand(time(NULL)); World w;

// Seed random number generator

// Randomly create 100 ants int antcount = 0; while (antcount < INITIALANTS) {

29 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

} // Randomly create 5 doodlebugs int doodlecount = 0; while (doodlecount < INITIALBUGS) { int x = rand() % WORLDSIZE; int y = rand() % WORLDSIZE; if (w.getAt(x,y)==NULL) // Only put doodlebug in empty spot { doodlecount++; Doodlebug *d1 = new Doodlebug(&w,x,y); } } // Run simulation forever, until user cancels while (true) { w.Display(); w.SimulateOneStep(); cout << endl << "Press enter for next step" << endl; getline(cin,s); } return 0;

int x = rand() % WORLDSIZE; int y = rand() % WORLDSIZE; if (w.getAt(x,y)==NULL) // Only put ant in empty spot { antcount++; Ant *a1 = new Ant(&w,x,y); }

2. Outline of Topics in the Chapter 15.1 Inheritance Basics Derived Classes Constructors in Derived Classes The protected Qualifier Redefinition of Member Functions Redefining Versus Overloading Access to a Redefined Base Function 15.2 Inheritance Details Functions that are not Inherited

30 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

Assignment Operator and Copy Constructor in a Derived Class Destructors in Derived Classes 15.3 Polymorphism Late Binding Virtual Functions in C++ Virtual Functions and Extended Type Compatibility Definitions for Every Virtual Member Function The text states that object-oriented programming is a powerful programming technique. Object Oriented Programming is of great importance in that it enables the solution domain to be closer to the problem domain, it facilitates code reuse, and it helps the programmer to better manage complexity through better abstraction and encapsulation. In our earlier study of data abstraction, we identified objects and classes. In C++, an object is a variable that has functions (behavior, usually exposed) and (hidden) data associated with it. We noted that classes encapsulate the essence of objects that are declared to be of that class's type. We characterized inheritance as creating a new class by "adding features to the features obtained from another class." We used the derivation of the ifstream class from istream, and the derivation of ofstream from ostream as examples of inheritance. We found that an object has identity (name, type), behavior (actions of the member functions), and state (the values of the variables defined in the class and set on behalf of our object.). This chapter studies the inheritance relationship that classes bear to other, that is, the is a relation. This chapter enables the student programmer to use inheritance in problem solving.

3. General Remarks on the Chapter

31 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

15.1 Inheritance Basics In the real world, objects exist in relation to one another. In solving problems we need to be as close to the problem as we can, within abstraction that allows us to ignore details to allows a solution of our problem. In our problem, we may describe one object as being like another, even saying one object "is a" type of some other object. The abstraction of the commonality among objects in C++ is done with classes, and abstraction of the commonality among objects that have distinguishing differences is expressed in C++ by the notion of inheritance. The commonalities are encapsulated in the base class, the distinctions are encapsulated in the derived class, and the commonality is passed from the base class to the derived class with inheritance. For example, we might think of a class of (general) vehicles, and then think of a kind of vehicles for carrying passenger (automobiles), another kind that is small and carries cargo in the open (pickup truck), still another that carries cargo in an enclosure (Van), others yet that carry cargo enclosed and are articulated -- tractor-trailer). The Truck, Van, and Car classes inherit their common features from the class vehicle. We see that inheritance creates a new class, called the derived class, that is an extension, specialization or modification of one or more existing classes, called the base classes. We call single inheritance the situation where there is only one base class. We will only mention multiple inheritance where there is more than one base class. In these cases, a car is a vehicle, so are pickups, vans, and tractor-trailers. We describe this relationship as an "is a" relation. A car "is a" vehicle; a pickup truck "is a" vehicle, and so on. The class abstraction of vehicle is the base class and the vehicles that bear the "is a" relation to the base class are derived classes. Each base class has features, that the derived class inherits those features and adds its own, extra, added features to the base class to form its distinctiveness. If we write this example in C++, we have

32 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

class Vehicle {/* . . . */}; class Car : public Vehicle { /* . . . */}; class Truck : public Vehicle { /* . . . */}; class TractorTrailer public Truck {/* . . . */}; class StationWagon : public Car {/* . . . */ }; class Boat : public Vehicle {/* . . .*/}; In this, example, we could illustrate multiple inheritance by defining class Emergency {/* . . . */}; and have had a class PoliceCar inherit from both class Car and class Emergency. Or we could have a class Ambulance inherit from both class Truck and class Emergency. The PoliceCar and Ambulance would inherit features from Car and the derived class adds its distinguishing features. The text only treats single inheritance, so we will not discuss this more general situation further. For further information on multiple inheritance, see Lippman and Lajoie, C++ Primer, 3rd edition or Stroustrup, The C++ Programming Language 3rd edition. Terminology: The roots of Object Oriented everything (Design, Analysis, Programming) are diverse. The result is that terminology is equally diverse. From Object Pascal, Objective C and Smalltalk, we get methods, and instance variables. From C++ we get member functions and member variables. From CLOS (Common Lisp Object System) we get generic functions. From Object Pascal and Smalltalk, we get superclass and subclass. In C++ we have base class and derived class. With the text, we will follow Stroustrup, using the terms base class and derived class, member function and member variable. Stroustrup finds it counter intuitive to have a 'subclass' that has more features than the 'superclass'. Access Specifiers If class Derived has base class Base:

33 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

class Base {/* . . .*/}; class Derived : { /* . . . */}; //where access-specifier is one of public, protected, private then the access that is accorded to member functions of class Derived depends on the access-specifier selected. Unfortunately, the access specifier for class inheritance defaults to private1. My students sometimes forget the access specifier, with results that disappoint and confuse them. Caution the students against omitting the access specifier. Constructors in Derived Classes The text notes that an object of a derived class type has more than one type. It has the type class of which it is defined, but it also has every member of the base class, without exception, and regardless of whether the members are public or private in the base class. Hence, a derived class object has both the base class part and the part added in the derivation. Both these parts of the object must be initialized. The derived class part of the object is initialized by the derived class constructor. The base class part of the object is initialized by the base class constructor. The text gives the syntax for invoking the base class constructor within the derived class constructor. Here is the way to pass values passed from a derived class constructor to a base class constructor. class Base { public: Base(int i, double d);
1

access-specifier Base

The access specifier for struct defaults to public. The text treats a struct much as if structs had only the attributes they have in C. It does not use struct member functions, for example. We will not pursue the use of structs with inheritance.

34 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

// other members }; class Derived : public Base { public: Derived(int i, double d); // other members }; Derived::Derived(int i, double d): Base(i, d) {// deliberately empty }

Constructor Base Class Initializer List Remind the students of the alternate initialization usage they saw first in Chapter 2, page 47: int count(0), limit(10); double distance(5.723), zymurgy_contant(1.234); which, in the simple case of declaration of variables, is an exact equivalent to int count = 0, limit = 10; double distance = 5.723, zymurgy_contant = 1.234; When initializing data members in a class, it is possible to initialize inside the constructor: class A { public: A() { i = 0;

35 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

j = 1; } private: int i; int j; }; The preferred method is to use base class initializer lists (also known as member initializer lists). Member/base class initializer lists can only be used with the constructor definition, not with the prototype (declaration) within the class. Stroustrup says (paraphrased) that Member initializers are required in situations where the semantics of initialization differs from the semantics of assignment. That is, member initializers must be used for members that are objects of classes that do not have default constructors, for const members, and for reference members. Cline, Lomow and Girou, in the C++ FAQs remark, "Initialize all member objects and base classes explicitly in the initialization list of a constructor. Not only does this explicitly express what the compiler is going to do anyway, but it is generally more efficient to initialize a member object properly than to use the default initialization followed by assignment. "The performance gain is substantial (as much as three time faster) compared to using default initialization followed by assignment [for user defined types]. There is no gain for built-in types, but there is no loss either, so initialization lists should be used for some." Redefining versus Overloading The distinction between overriding and redefinition is simply what happens when a class object a) is accessed through a reference, such as when passed by reference, or

36 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

b) is invoked through a pointer to base class objects but is initialized to point to a derived class object, or though the implicit this pointer within a class member function. It is this last situation where the student will find surprises while learning these ideas. We rephrase some definitions from the text: Redefining: If a base class and a derived class each have a function with the same signature, and the base class member function is not declared virtual the derived class function redefines the base class function. Overriding: If a base class and a derived class each have a function with the same signature, and the base class member function is declared virtual the derived class function overrides the base class function. One excellent example of this situation is in the text in Display 15.6. Here, class Figure is the base class, and class Box and class Triangle are derived from class Figure. Each of Box and Triangle inherit the member function center()from Figure. The member center()in class Figure is declared a virtual function. Consequently when center()is called on behalf of a Box object, the Box member function center()is called. 15.2 Inheritance Details Functions that are not inherited. Specifically, constructors, operator assignment and destructors are not inherited. If you think about it, you will realize that these members are so connected to the class at hand, that only class with no data members derived from some base class could inherit these members. A copy constructor, an operator assignment, and a destructor, while not inherited, the compiler will generate a version for any of these members that you do not define in your class. The compiler generated member functions will do a "shallow copy" or "member-wise copy". This means that the members are copied according to the defined rule for the type of the member. Primitive types such as int are just copied. A

37 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

<string> object is copied correctly, but if you have a pointer to a dynamically allocated data structure, only the pointer will be copied. The object and its copy will have pointers to the same free store memory. The data structure is not copied. I have pointed out earlier that one of my students called this "member UN-wise copy" because of the danger involved. Assignment Operator and Copy Constructor in a Derived Class It is easier to write a correct implementation of a copy constructor or assignment operator for a derived class if you use the corresponding member from the base class to copy the base class part of the object. Destructors in Derived Classes I have only one word to add to the text's remarks on destructors in an inheritance hierarchy. The destructor in the base class should be virtual. In spite of the fact that the destructor is named different in each class in an inheritance hierarchy, the behavior of the destructors is very much as if they were all named the same, and only the base class destructor will be called if you don't make the base class destructor virtual 15.3 Polymorphism Polymorphism is composed of the prefix poly- meaning many, and the root word, -morphmeaning shape, and -ism, an action suffix. The text says, " the ability to associate multiple meanings with one function name." The text points out that function name overloading is polymorphism in this more general sense. Further, there is the narrower sense in that polymorphism is associated with late binding of member function names to various member functions up an inheritance chain. Late Binding If, in the base class, we make a member function virtual, then in the derived classes have a member functions that has the same signature as the virtual function in the base class, the derived class members are also virtual, and we may derive as many times a needed. Suppose a pointer variable of type pointer to base class is declared. If the pointer variable

38 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

is assigned a pointer to an object down the inheritance chain, and the virtual member function is called through this pointer, late binding will select the member function that matches the particular object's type from among the several choices. Suppose we have define a function with parameter of type reference to base class is declared. Let this function invoke a virtual member function through the reference parameter. Now suppose an object of type somewhere down the inheritance chain is passed to the function. Then the late binding facility will select the correct the member function that matches the particular object's type from among the several functions. Virtual Functions in C++ C++ manages the selection described in the previous paragraph by means of a table, called the virtual table. This table is created only if we declare the function to with the keyword virtual, so we only get the late binding effect if we have all the signatures the same and supply the keyword virtual in the base class. Virtual Functions and Extended type Compatibility The Slicing Problem We pointed out that because a derived class object is already of base class type, the address of either a base class object or a derived class object may be assigned to a variable of type pointer to the base class. It is also true that a derived class object can be assigned to a variable of base class type. Are there consequences? In short, yes. Why? Information is lost. Remember that a derived class object has two types. It has a "slice" that is gained in the inheritance and the rest comes from the base class. If we assign a derived class object to a base class variable, then where does the slice that is gained in the inheritance go? This is not a rhetorical question, for there is no place in the variable to contain that information. The answer is, the inherited slice is lost. This is the slicing problem. Let us extend the example from above.

39 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

class B { public: B(); virtual void f(); // all derived class member functions having // this signature are virtual private: int b; }; B::B():b(999){} class D : public B { public: D(); void f()// this is a virtual function private: int d; }; D::D() : B(), d(0){} Instead of assigning addresses as we did earlier, let's declare and assign objects: B beta // default constructor called D delta // default constructor called // delta = beta; // error, not assignment compatible beta = delta; // legal but were does delta.d go? The slicing problem has bit off the delta.d piece in the assignment. Defeating the Slicing Problem. Observe that a pointer may have type pointer to base class, but when we assign the address of a derived class object to a base class pointer, it is the pointer that is assigned, not the object. If we use a derived object for an argument in a

40 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Savitch Problem Solving w/ C++, 6e

Instructors Resource Guide Chapter 15

function that has a base class parameter, the reference is initialized, but the object is not changed. We do not lose information. Slicing does not occur. Slicing occurs when we assign objects. It does not occur when we assign pointers or initialize references. If a virtual member function is called through a pointer or a reference to the base class, the virtual mechanism will call the function that corresponds to the type object to which the pointer points.

41 Copyright 2007 Pearson Education, Inc. Publishing as Pearson Addison-Wesley

Potrebbero piacerti anche