diff options
-rw-r--r-- | 03-09.md | 4 | ||||
-rw-r--r-- | 03-11.md | 216 |
2 files changed, 220 insertions, 0 deletions
@@ -548,3 +548,7 @@ herbivore::herbivore(double init_size, double init_rate, double init_need) : ani //No work is done here, except calling the animal constructor } ``` + +--- + +[03/11 ->](03-11.md) diff --git a/03-11.md b/03-11.md new file mode 100644 index 0000000..042c6a1 --- /dev/null +++ b/03-11.md @@ -0,0 +1,216 @@ +[\<- 03/09](03-09.md) + +--- + +# Virtual Member Functions + +## Example + +``` +class person{ + public: + person(std::string name = "empty", int age = 0){ + this->name = name; + this->age = age; + } + + void print_name() const { + std::cout << "Name is: " << get_name() << std::endl; + } + + protected: + std::string get_name() const { return name; }; + + private: + std::string name; + int age; +}; + +class professor : public person{ + public: + professor(std::string name = "empty", int age = 40, double salary = 0, bool nice = true) : person(name, age), salary(salary), nice(nice){}; + + //Overriding the get_name function + //Now this is a public function of this class + std::string get_name() const{ + return (person::get_name() + ", he is nice!"); + } + + private: + double salary; + bool nice; +}; + +int main(int argc, const char* argv[]){ + professor myProf("Ben", 36); + myProf.print_name(); + return 0; +} + +--- + +//The output is: +Name is: Ben +``` + +## An example with a virtual + +``` +class person{ + public: + person(std::string name = "empty", int age = 0){ + this->name = name; + this->age = age; + } + + void print_name() const { + std::cout << "Name is: " << get_name() << std::endl; + } + + protected: + //This is a virtual now!!! + virtual std::string get_name() const { return name; }; + + private: + std::string name; + int age; +}; + +class professor : public person{ + public: + professor(std::string name = "empty", int age = 40, double salary = 0, bool nice = true) : person(name, age), salary(salary), nice(nice){}; + + //Overriding the get_name function + //Now this is a public function of this class + std::string get_name() const{ + return (person::get_name() + ", he is nice!"); + } + + private: + double salary; + bool nice; +}; + +int main(int argc, const char* argv[]){ + professor myProf("Ben", 36); + myProf.print_name(); + return 0; +} +``` + +- If we use `virtual std::string get_name() const { return name; };`, then the output is: + - `Name is: Ben, he is nice!` + +- If we use `std::string get_name() const { return name; };`, then the output is: + - `Name is: Ben` + +## Example: game Class + +- **Virtual member functions** are a new kind of member function that allows certain aspects of activating a function to be delayed until a program is actually running +- To make the explanation of virtual functions concrete, we'll present an example of a class called **game**, which will mke it easier for us to write programs that play various two-player games such as chess, checkers, or Othello + +``` +class game{ + public: + //ENUM TYPE + enum who { HUMAN, NEUTRAL, COMPUTER }; //Possible game outcomes + + //CONSTRUCTOR and DESTRUCTOR + game() { move_number = 0; }; + virtual ~game() {}; + + //PUBLIC MEMBER FUNCTIONS + //The play function should not be overridden. It plays once game, with the human player moving first and the computer second + //The computer uses an alpha-beta look ahead algorithm to select its moves. The return value is the winner of the game (or NEUTRAL for a tie) + who play(); + + protected: + //OPTIONAL VIRTUAL FUNCTIONS (overriding these is optional) + virtual void display_message(const std::string& message) const; + virtual std::string get_user_move() const; + virtual who last_mover() const { return (move_number % 2 == 1 ? HUMAN : COMPUTER); }; + virtual int moves_completed() const { return move_number; }; + virtual who next_mover() const { return (move_number % 2 == 0 ? HUMAN : COMPUTER); }; + virtual who opposite(who player) const { return (player == HUMAN ? COMPUTER : HUMAN); }; + virtual who winning() const; + + //VIRTUAL FUNCTIONS THAT MUST BE OVERRIDDEND (The overriding function function should call the original when it finishes) + //Have the next player make a specified move: + virtual void make_move(const std::string& move) { ++move_number; }; + //Restart the game from the beginning: + virtual void restart() { move_number = 0; }; + + //PURE VIRTUAL FUNCTIONS (these must be provided for each derived class + //Return a pointer to a copy of myself: + virtual game* clone() const = 0; + //Computer all the moves that the next player can make: + virtual void computer_moves(std::queue<std::string>& moves) const = 0; + //A pure virtual function is indicated by "= 0" before the end of the semicolon in the prototype + //The class does not provide any implementation of a pure virtual function + + private: + //MEMBER VARIABLES + int move_number; //Number of moves made so far + //... +}; +``` + +## Protected Members + +``` +protected: + //OPTIONAL VIRTUAL FUNCTIONS (overriding these is optional) + virtual void display_message (const std::string& message) const; +``` + +- The keyword **protected** indicates that the items that follow will be a new kind of member, somewhat between *public* and *private* +- A protected member can be used (and overridden) by a derived class - but apart from within a derived class, any protected member is private; It cannot be used elsewhere + +## Virtual Member Functions + +- The first protected member function in the game class is `display_message` + +``` +game::who game::play(){ + display_message("Welcome!"); + ... +} +``` + +- When we play the game, the welcome message is printed at the start +- But, suppose that we write a game that needs to display messages by some other method +- Our derived class will inherit all the game class members, but it can override any method that it doesn't like + +- Perhaps we want our Connect Four game to print "\*\*\*" before each displayed message (just to make the messages more exciting) +- We could override the `display_message` function so that `connect4::display_message` first prints "\*\*\*" and then prints the message +- With this approach, when the `play` method begins, it will activate `display_message("Welcome!")`, but what will this print? + - If it uses `game::display_message`, then the word "Welcome!" appears by itself + - If it uses `connect4::display_message`, then "\*\*\*Welcome!" + +### When Onve Member Function Activates Another + +- Normally, when a member function `f()` activates another member function `g()`, the version of `g()` that is actually activated comes from the same class as the function `f()` +- This behavior occurs even if the activated function `g` is later overridden, and `f` is activated by an object of the derived class +- In this example, this means that `game::play` normally would use `game::display_message`, even if a derived object activates the play method +- Solution: use "**virtual**" + +### Activating a Virtual Member Function + +- Whenever a **virtual** member function is activated, **the data type of the activating object is examined when the program is running**, and the correct version of the function is used +- For a virtual member function, the choice of which version of the function to use is never made until the program is running +- At run time, **the program examines the data type of the object** that activated the method, and thereby uses the correct version of the function + +### Pure Virtual Functions and Abstract Classes + +- A pure virtual function is indicated by "= 0" before the end of the semicolon in the prototype +- The class does not provide any implementation of a pure virtual function +- Because there is no implementation, any class with a pure virtual function is called an **abstract class** and **no instances of an abstract class may appear in a program** +- Abstract classes are used as base classes, and it is up to the derived class to provide an implementation for each pure virtual function + +## Summary + +- Object-oriented programming supports reusable components by permitting new derived classes to be declared, which automatically inherit all members of an existing base class +- All members of a base class are inherited by the derived class, but only the non-private members of the base class can be accessed by the programmer who implements the derived class +- The connection between a derived class and its base class can often be characterized by the "is-a" relationship +- An abstract base class (such as the game class) can provide a common framework that is needed by many derived classes +- An abstract base class has one or more pure virtual functions, which are functions that must be overridden before the class can be used |