210 likes | 316 Views
Informatik I (D-ITET) Group 7, 15:00-17:00, ETZ H91 Assistant: Ercan Ucan. Slides at http://people.inf.ethz.ch/eucan/inf-1 /. Exercise 11 (10/12/2012) . Inheritance.
E N D
Informatik I (D-ITET)Group 7, 15:00-17:00, ETZ H91Assistant: Ercan Ucan Slides at http://people.inf.ethz.ch/eucan/inf-1/ Exercise 11 (10/12/2012)
Inheritance Class inheritance is the way C++ enables code modification for extending and/or modifying existing classes. Inheritance allows to create classes which are derived from other classes, so that they automatically include the methods and the data of the old classes. The existing parent-classes, which the new ones extend, are called base classes, and the children that inherit, derived classes. CPolygon BASE CLASS CTriangle cCRectangle DERIVED CLASSES
Inheritance - Example #include <iostream> using namespace std; // BASE CLASS class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b;} }; //DERIVED CLASS class CRectangle: public CPolygon{ public: int area () { return (width * height); } }; //DERIVED CLASS class CTriangle: public CPolygon{ public: int area () { return (width * height / 2); } }; int main () { CRectanglerect; CTriangletrgl; rect.set_values (4,5); trgl.set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; } The colon (:) indicates that the derived class inherits from the base class, and in the example that class CPolygon is a base class and CRectangle is a derived class inheriting from CRectangle.
Access Identifiers • The protected keyword is like private in the sense that the outside world can access class members in a protected section only by using public class members. • The difference comes into play only within classes derived from the base class. • When a class inherits from a base class, the members of the derived class can access the protected members inherited from the base class, but of course not its private members.
Inheritance - Example #include <iostream> using namespace std; // BASE CLASS class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b;} }; //DERIVED CLASS class CRectangle: public CPolygon{ public: int area () { return (width * height); } }; //DERIVED CLASS class CTriangle: public CPolygon{ public: int area () { return (width * height / 2); } }; int main () { CRectanglerect; CTriangletrgl; rect.set_values (4,5); trgl.set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; } The objects of the classes CRectangle and CTriangle inherit the members: width, height andset_values() from the base class CPolygon.
Inheriting access levels • The public keyword after the colon (:) , e.g. class CRectangle: public Cpolygon, means that the public members of the base class become public members of the derived class. • If you specify a more restrictive access like protected, e.g. class CRectangle: protected Cpolygon, means that the public members of the base class become protected in the derived class and • If you specify an even more restrictive access like private, e.g. class CRectangle: private Cpolygon, means that all the members of the base class become private in the derived class.
Inheritance - Example #include <iostream> using namespace std; // BASE CLASS class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b;} }; //DERIVED CLASS class CRectangle: public CPolygon{ public: int area () { return (width * height); } }; //DERIVED CLASS class CTriangle: public CPolygon{ public: int area () { return (width * height / 2); } }; int main () { CRectanglerect; CTriangletrgl; rect.set_values (4,5); trgl.set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; } The public members of the base class Cpolygon have become public members of the derived Class CRectangle.
Inheriting Constructors • When creating an object of a derived class, the program first calls the base-class constructor and then the derived-class constructor. • The base-class constructor is responsible for initializing the inherited data members. • The derived-class constructor is responsible for initializing any added data members. • A derived-class constructor always calls a base-class constructor. • You can use the initializer list syntax to indicate which base-class constructor to use, e.g. • derived::derived(type1 x, type2 y) : base(x,y) //Initializer list • With this syntax the constructor of the class derived passes values along to the constructor of the base-class. • If you don’t supply a base-class constructor in a member initializer list, the program uses the default base-class constructors.
Inheriting Constructors - Example // constructors and derived classes #include <iostream> using namespace std; //BASE CLASS class mother { public: mother () { cout << "mother: no parameters\n"; } mother (int a) { cout << "mother: int parameter\n"; } }; //DERIVED CLASS class daughter : public mother { public: daughter (int a) { cout << "daughter: int parameter\n\n"; } }; //DERIVED CLASS class son : public mother { public: son (int a) : mother (a) { cout << "son: int parameter\n\n"; } }; int main () { daughter cynthia (0); son daniel(0); return 0; } Output: mother: no parameters daughter: int parameter mother: int parameter son: int parameter
Virtual Functions • A virtual function is a function whose behavior can be overridden within an inheriting class by a function with the same signature. The virtual nature is inherited in the subsequent derived classes and the virtual keyword needs not be re-stated here. Binding refers to the act of associating an object or a class with its member. If we can call a method fn() on an object o of a class c, we say that the object o is binded with the method fn(). This happens at compile time and is known as static or compile - time binding. The calls to the virtual member functions are resolved during run-time. This mechanism is known as dynamic binding. The most prominent reason why a virtual function will be used is to have a different functionality in the derived class. The difference between a non-virtual member function and a virtual member function is, the non-virtual member functions are resolved at compile time.
Virtual Functions - Example #include <iostream.h>class base { public:virtual void display() {cout<<”\nBase”; }}; class derived : public base { public:void display() {cout<<”\nDerived”; }};void main() { base *ptr = new derived();ptr->display(); } In the above example, the pointer is of type base but it points to the derived class object. The method display() is virtual in nature. Hence in order to resolve the virtual method call, the context of the pointer is considered, i.e., the display method of the derived class is called and not that of the base. If the method was non virtual in nature, the display() method of the base class would have been called.
Virtual Functions – The Wikipedia Example #include <iostream> #include <vector> using namespace std; class Animal { public: virtual void eat() const { cout << "I eat like a generic Animal." << endl; } virtual ~Animal() {} }; class Wolf : public Animal { public: void eat() const { cout << "I eat like a wolf!" << endl; } }; class Fish : public Animal { public: void eat() const { cout << "I eat like a fish!" << endl; } };
Virtual Functions – The Wikipedia Example class OtherAnimal : public Animal { }; int main() { std::vector<Animal*> animals; animals.push_back( new Animal() ); animals.push_back( new Wolf() ); animals.push_back( new Fish() ); animals.push_back( new OtherAnimal() ); for( std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) { (*it)->eat(); delete *it; } return 0; } Output if virtual function Animal::eat() I eat like a generic Animal. I eat like a wolf! I eat like a fish! I eat like a generic Animal. Output if Animal::eat() were not declared as virtual: I eat like a generic Animal. I eat like a generic Animal. I eat like a generic Animal. I eat like a generic Animal.
Virtual Functions – What to remember • Use the virtual function for the member functions of the base-class that you may have to redefine in derived-classes. • The calls to the virtual member functions are resolved during run-time. • Constructors cannot be virtual. • Destructors should be virtual unless a class isn’t to be used as a base class. In this way you ensure that the destructors are called in the correct order. Remember that the destructors are called in the reverse order of inheritance.
const Member Functions • If you want to define a function that promises not to modify the invoking object you can use the following syntax: • intgetVal(unsigned int r, unsigned int c) const; • … • int Board::getVal(unsigned int r, unsigned int c) const { • … • }
Exercise 1 – Tic-tac-toe • In the lecture page you will find the file tictactoe.cpp that implements a simple (randomized) version of the game. The game rules are simple: • It’s like a chess played only with pawns. • You win if you reach the opponent’s side or if your opponent cannot move. • Basically if you run the code (or if you read it) you will see that there exist two computer players which play randomly in turns until either one has won or it’s a draw. There are the following functions implemented: • • void drawGame(player board [3] [3]):Prints the current board configuration on the screen. • • boolisValidMove(player board [3] [3], player p, move m):Checks if a move m from player p is valid. • boolmakeMove (player board [3] [3], player p): Performs a move from player p on the board. • boolhasReachedOtherSide (player board [3] [3], player p): Checks to see if player p has reached other side.
Exercise 1 – Tic-tac-toe // enum for the player type enum player { empty, player1, player2 }; // struct for a move structmove { intsx,sy,dx,dy; }; boolmakeMove(player board[3][3], player p) { // reserve space for all possible moves move* moves = new move[81]; int count = getValidMoves(board, p, moves); if(count > 0) { // perform move intmoveChosen = rand() % count; board[moves[moveChosen].sx][moves[moveChosen].sy] = empty; board[moves[moveChosen].dx][moves[moveChosen].dy] = p;return true; } else { return false; } }
Exercise 1 – Tic-tac-toe (1.1) You have to convert the program so that it follows the object oriented programming principles. You are given the header file tictactoeOO.hwhich declares the classes and their member function and an incomplete .cpp where you have to implement these functions. The following classes with their respective methods need to be implemented: • Board Class: • Board (void); //The constructor – initializes the board for a new game. • Void print(); //Prints the current state of the game on the console. • IntgetVal(int x, int y); //Gets the current state of the field defined by row y and column x. • Void setVal(int x, int y, player p); //Sets the field (x,y) to the value p. • Void isValidMove(player p, move m); //Tests if the move is valid. • boolhasWon(player p); //Tests if the player p has reached the opponent’s side. • Player Class: • Player (player p); //Constructor to create one of the two player objects. • player getPlayer(); //Returns the current player • boolmakeMove(Board *board); //Performs a move on the board. • Game Class: • Game (Player *p1, Player *p2); //Constructor to create a game between two Player objects. • Player *play(); //Implements the game – returns the winnter of a NULL for draw.
Exercise 1 – Tic-tac-toe • (1.2) You have to use the inheritance feature of C++ to create different player classes. • More specifically you have to create a base-class AbstractPlayer (in the same way as Player in 1.1). But denote the makeMove() function as virtual and leave its implementation blank. This is where the derived classes will differ. More specifically, derive the following Classes: • RandomPlayer -> As the players before he performs random moves on the board. • SmartPlayer -> Try to make this player as smart as possible. An easy thing to implement is to look forward by one move. If you can find a winning next move do it, if you can find that the opponent can win in the next move block him, otherwise play randomly. • HumanPlayer -> Just read from cin where the human player wants to play and make the move.
Exercise 1 – Tic-tac-toe class AbstractPlayer{ private: player p; protected: intgetValidMoves(Board *board, move* moves); public: player getPlayer() const; AbstractPlayer(player p); virtual ~AbstractPlayer() {}; virtual boolmakeMove(Board *board) { }; class RandomPlayer : public AbstractPlayer{ public: RandomPlayer(player p) : AbstractPlayer(p) {}; virtual ~RandomPlayer() {}; virtual boolmakeMove(Board *board); };
Exercise 1 – Tic-tac-toe // enum-Typfuer die beidenSpieler enum player {noone, player1, player2}; // Klassen-Definition Board class Board { private: player board[3][3]; static const char symbol[3]; protected: void drawRow(unsigned inti) const; public: Board(); void print() const; intgetVal(unsigned int r, unsigned int c) const; boolsetVal(unsigned int r, unsigned int c, player val); player getWinner() const; }; const char Board::symbol[3] = {' ', 'X', 'O'}; class AbstractPlayer { private: player p; public: player getPlayer() const; AbstractPlayer(player playerNumber); virtual ~AbstractPlayer() {}; virtual void makeMove(Board &b) {}; }; // Klassen-Definition RandomPlayer class RandomPlayer : public AbstractPlayer { public: RandomPlayer(player playerNumber) : virtual ~RandomPlayer() {}; virtual void makeMove(Board &b); }; …………………………… // Here put the definitions for the other classes // Klassen-Definition Game class Game { private: AbstractPlayer *p1, *p2; Board b; public: Game(AbstractPlayer &p1, AbstractPlayer &p2); AbstractPlayer* play(); }; #endif