Inheritance
Chapter Goals To understand the concepts of inheritance and polymorphism To learn how to inherit and override member functions To be able to implement constructors for derived classes To be able to design and use virtual functions
Inheritance Hierarchies
Inheritance Hierarchies Shovels, rakes, and clippers all perform gardening tasks. They can be considered as specialized versions of the general ‘gardening tool’ type.
Inheritance Hierarchies In object-oriented design, inheritance is a relationship between a more general class (called the base class) and a more specialized class (called the derived class). The derived class inherits data and behavior from the base class.
Inheritance IS-A
Inheritance: The IS-A Relationship All cars are vehicles. (This is correct and good English.)
Inheritance: The IS-A Relationship All cars IS-A vehicles. (Correct and … um … English?)
Inheritance: The IS-A Relationship You may recall the UML notation HAS-A for containment. IS-A denotes inheritance.
Inheritance: The IS-A Relationship All cars IS-A vehicles. (Correct… …when speaking object ively)
Inheritance: The IS-A Relationship All Cars are Vehicles. All Motorcycles are Vehicles. All Sedans are Vehicles. C++ for Everyone by Cay Horstmann Copyright © 2012 by John Wiley & Sons. All rights reserved
Inheritance: The IS-A Relationship Vehicles is the base class. Car is a derived class. Truck derives from Vehicle.
Inheritance: The IS-A Relationship SUV derives from Car, which derives from Vehicle.
Inheritance: The IS-A Relationship Truck IS-A Vehicle. SUV IS-A Car.
Inheritance: The IS-A Relationship Everything about being a Vehicle is inherited by Cars and Trucks and SUVs.
Inheritance: The IS-A Relationship Those things specific to Cars are only inherited by Sedans and SUVs.
Inheritance Hierarchies I inherited my ROYALNESS from my parents who inherited it from their parents.
Inheritance Hierarchies I’m a special version of my base class. I’m very special.
The Substitution Principle Suppose we have an algorithm that manipulates a Vehicle object. Since a car IS-A vehicle, we can supply a Car object to such an algorithm, and it will work correctly. C++ for Everyone by Cay Horstmann Copyright © 2012 by John Wiley & Sons. All rights reserved
The Substitution Principle The substitution principle states that you can always use a derived-class object when a base-class object is expected.
The Substitution Principle Did you know you have already been working with class hierarchies? (No! Really?)
The Substitution Principle Remember your friends cin and cout? (Yes.) Their types are in an inheritance chain. (Chains? Like prisoners?)
The Substitution Principle No, silly. That’s just another phrase for inheritance hierarchy.
The Substitution Principle Look: void process_input(istream& in); You can call this function with an ifstream object or with an istream object. Why?
The Substitution Principle Because istream is more general than ifstream. void process_input(istream& in); This works by inheritance:
The C++ Stream Hierarchy istream is the base class of ifstream. ifstream inherits from istream.
Quiz Time OK. QUIZ TIME!
Everyone likes taking quizzes. Quiz Time OK. QUIZ TIME! Everyone likes taking quizzes. So let’s take one. (Oh no!)
You don’t like taking quizzes? Quiz Time You don’t like taking quizzes? (Not really …) OK Let’s create a Quiz Question hierarchy. (Whew!)
Design Phase of Question Program We will try to make this as general as we can because often quizzes consist of different kinds of questions: (We like multiple guess questions.) Fill-in-the-blank Choice (single or multiple) Numeric (we’ll allow approximate answers to be OK) Free response
Design Phase of Question Program Here is the UML diagram that resulted from our analysis:
The Base Class: Question At the root of this hierarchy is the Question type.
The Base Class: Question We want a object of Question type to work like this: First, the programmer sets the question text and the correct answer in the Question object. C++ for Everyone by Cay Horstmann opyright © 2012 by John Wiley & Sons. All rights reserved
The Base Class: Question Then, when it’s time to run the testing program with a user taking the test, the programmer asks the Question to display the text of the question: Who was the inventor of C++? The Question object displays this
The Base Class: Question That programmer then gets the use’s response and passes it to the Question object for evaluation: Who was the inventor of C++? Your answer: Bjarne Stroustrup true The Question object displays this
The Base Class: Question – Member Variables To work as shown, a Question object would contain: The question’s text string text; The correct answer string answer;
The Base Class: Question – Constructor string text; string answer; What initial values should these have? What could possibly be a reasonable initial value? And – we’ll write mutators to allow setting them later. So a default Question constructor that does nothing is fine. The string class constructor gives us empty strings.
The Base Class: Question – Accessors A Question object should be able to: Display its text void display() const; Check whether a given response is a correct answer bool check_answer(string response) const;
The Base Class: Question – Mutators And have these mutators: Set the question’s text void set_text(string question_text); Set the correct answer void set_answer(string correct_response);
The Base Class: Question – { public: Question(); void set_text(string question_text); void set_answer(string correct_response); bool check_answer(string response) const; void display() const; private: string text; string answer; };
Question Class Test Program Here’s a complete program to test our Question class. #include <iostream> #include <sstream> #include <string> using namespace std; class Question { public: /** Constructs a question with empty text and answer. */ Question();
Question Class Test Program /** @param question_text the text of this question */ void set_text(string question_text); @param correct_response the answer to this question void set_answer(string correct_response); @param response the response to check @return true if the response was correct,false otherwise bool check_answer(string response) const;
Question Class Test Program /** Displays this question. */ void display() const; private: string text; string answer; }; Question::Question() { }: void Question::set_text(string question_text) text = question_text; }
Question Class Test Program void Question::set_answer(string correct_response) { answer = correct_response; } bool Question::check_answer(string response) const return response == answer; void Question::display() const cout << text << endl;
Question Class Test Program int main() { string response; // Show Boolean values as true, false cout << boolalpha; Question q1; q1.set_text("Who was the inventor of C++?"); q1.set_answer("Bjarne Stroustrup"); q1.display(); cout << "Your answer: "; getline(cin, response); cout << q1.check_answer(response) << endl; return 0; }
boolalpha Did you notice this in the code? // Show Boolean values as true, false cout << boolalpha; The boolalpha manipulator causes Boolean values to be displayed as the output strings: "true" for true and "false" for false – otherwise, numbers would be displayed.
Implementing Derived Classes Now for those different kinds of questions. All of the different kinds IS-A Question so we code by starting with the base class (Question) and then we write code for what makes them special versions of more general Question type.
Implementing Derived Classes I know about being special.
Implementing Derived Classes Through inheritance, each of these types will have the data members and member functions set up in class Question. – plus “specialness-es” (We don’t rewrite the member functions) (code reuse in action – all right!)
Implementing Derived Classes That’s me!
Implementing Derived Classes We will start with the “choice question” kind of question: class ChoiceQuestion : public Question { public: // New and changed member // functions will go here private: // Additional data members // will go here };
Implementing Derived Classes I’m his parent: the base class Implementing Derived Classes The : symbol denotes inheritance. I’m a derived class class ChoiceQuestion : public Question { public: // New and changed member // functions will go here private: // Additional data members // will go here };
Implementing Derived Classes The keyword public makes sure this is an IS-A relationship. class ChoiceQuestion : public Question { public: // New and changed member // functions will go here private: // Additional data members // will go here }; ChoiceQuestion IS-A Question
Implementing Derived Classes We are telling the compiler to start with the Question class and add these things to it. class ChoiceQuestion : public Question { public: // New and changed member // functions will go here private: // Additional data members // will go here };
Implementing Derived Classes – Analysis of the Problem So what are these new things? How does a ChoiceQuestion differ from its base class? It’s use in the interaction with a user will be different:
Implementing Derived Classes – Analysis of the Problem After a programmer has set the question text and the several multiple choice answers the ChoiceQuestion object is asked to display: In which country was the inventor of C++ born? 1: Australia 2: Denmark 3: Korea 4: United States The Question object displays all this
Implementing Derived Classes – Analysis of the Problem The programmer then gets the user’s input, and sends it to the ChoiceQuestion object to see if it is correct. In which country was the inventor of C++ born? 1: Australia 2: Denmark 3: Korea 4: United States Your answer: 2 true The Question object displays this
Implementing Derived Classes – Coding The code will have to make ChoiceQuestion be a specialized form of a Question object.
Implementing Derived Classes ChoiceQuestion must have: Storage for the various choices for the answer Question has the text and correct answer, not these A member function for adding another choice A display function The designer of the Question class could not have known how to display this sort of multiple choice question. It only has the question itself, not the choices. In the ChoiceQuestion class you will have to rewrite the display function display. This is called overriding a member function.
Implementing Derived Classes – Coding After specifying the class you are inheriting from, you only write the differences: class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
Implementing Derived Classes – Coding Where is the set_text member function? Where is the string text; data member? Right there class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
ChoiceQuestion is one type, Derived Classes class Question { public: Question(); void set_text(string question_text); void set_answer(string correct_response); bool check_answer(string response) const; void display() const; private: string text; string answer; }; ChoiceQuestion is one type, made of two subtypes. class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
It is based on the Question type so it has those parts. Derived Classes members from class Question class Question { public: Question(); void set_text(string question_text); void set_answer(string correct_response); bool check_answer(string response) const; void display() const; private: string text; string answer; }; It is based on the Question type so it has those parts. class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
And, added to those parts from the Question type, Derived Classes specializations class Question { public: Question(); void set_text(string question_text); void set_answer(string correct_response); bool check_answer(string response) const; void display() const; private: string text; string answer; }; And, added to those parts from the Question type, it has its own specializations (its specialness-es). class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
Derived Classes to make one type: ChoiceQuestion v class Question { public: Question(); void set_text(string question_text); void set_answer(string correct_response); bool check_answer(string response) const; void display() const; private: string text; string answer; }; to make one type: ChoiceQuestion class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
Derived Classes – Syntax
Implementing Derived Classes – Coding The derived class inherits all data members and all functions that it does not override.
Implementing Derived Classes – Coding But… private means private.
Implementing Derived Classes – Coding Consider: Choice_question choice_question; choice_question.set_answer("2"); (OK, a public method in a derived part.)
Implementing Derived Classes – Coding How about: Choice_question choice_question; choice_question.answer = "2"; (Well, it did inherit that data member …)
Implementing Derived Classes – Coding No, private means private! Choice_question choice_question; choice_question.answer = "2"; // will not compile - private (OK – private means private even if inherited.)
Implementing Derived Classes – Coding This means that when you are writing the ChoiceQuestion member functions, you cannot directly access any private data members in Question. (Oh, dear. What to do?)
Implementing Derived Classes – Coding A good design would be for this function to take a choice and somehow an indication that this choice is the correct answer. (bool! bool! bool!)
Implementing Derived Classes – Coding Very good: ...but void ChoiceQuestion::add_choice(string choice, bool correct) { choices.push_back(choice); if (correct) // Convert choices.size() to string ostringstream stream; stream << choices.size(); string num_str = stream.str(); // Set num_str as the answer ... }
Implementing Derived Classes – Coding Oh dear answer is private! void ChoiceQuestion::add_choice(string choice, bool correct) { choices.push_back(choice); if (correct) // Convert choices.size() to string ostringstream stream; stream << choices.size(); string num_str = stream.str(); // Set num_str as the answer ... }
Implementing Derived Classes – Coding Happily, the designer of Question provided accessors! void ChoiceQuestion::add_choice(string choice, bool correct) { choices.push_back(choice); if (correct) // Convert choices.size() to string ostringstream stream; stream << choices.size(); string num_str = stream.str(); // Set num_str as the answer set_answer(num_str); } implicit parameter.set_answer(num_str);
Here is the class definition for ChoiceQuestion again. Common Error Here is the class definition for ChoiceQuestion again. It’s wrong Can you find it? – we made a small mistake. class ChoiceQuestion : Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
Common Error: Private Inheritance Aha! class ChoiceQuestion : Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
Common Error: Private Inheritance If you do not specify public inheritance, you get private inheritance and everything is a mess. class ChoiceQuestion : private Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };
Common Error: Private Inheritance If you do not specify public inheritance, you get private inheritance and everything is a mess. (Be careful, son!)
Common Error: Replicating Base Class Members You know all about private access: A derived class has no access to the private data members of the base class.
Common Error: Replicating Base Class Members But when some programmers encounter a compiler error, They just start hacking. they (not you, of course) don’t stop and THINK. ChoiceQuestion::ChoiceQuestion(string quest_txt) { text = quest_txt; } COMPILER ERROR: accessing private data member text
Common Error: Replicating Base Class Members And an “easy” fix seems to be to add the data member that the compiler is complaining about. ChoiceQuestion::ChoiceQuestion(string quest_txt) { text = quest_txt; } COMPILER ERROR: accessing private data member text
Common Error: Replicating Base Class Members And an “easy” fix seems to be to add the data member that the compiler is complaining about. class ChoiceQuestion : public Question { ... private: vector<string> choices; string text; }
Common Error: Replicating Base Class Members Oh dear! TWO test data members!
Common Error: Replicating Base Class Members One set by the constructor and the other displayed! Oh dear! Oh Dear! OH DEAR!!!
Inheritance Is For Behaviors, Not Values As you start working with inheritance, don’t overdo it! Consider a fleet of taxis, some are blue and some are those regular New York yellow cabs, but they all IS_A taxi.
Inheritance Is For Behaviors, Not Values Consider doing this: class Taxi { ... }; class BlueTaxi : public Taxi { ... }; class NYCYellowTaxi : public Taxi { ... };
Inheritance Is For Behaviors, Not Values What’s the only difference between a BlueTaxi and an NYCYellowTaxi? The color. That’s is not a behavior, it’s a value. Don’t create classes based on values. Make the value be a data member.
Inheritance Is For Behaviors, Not Values The solution to the BlueTaxi, NYCYellowTaxi confusion: class Taxi { ... private: String color; };
Inheritance Is For Behaviors, Not Values Create classes based on behaviors: like livery and metered cabs. class Cab { ... }; class MeteredCab : public Cab { ... }; class LiveryCab : public Cab { ... };
Calling the Base-Class Constructor Consider constructing a derived-class. Like all constructors, they don’t construct: they initialize. But a derived-class constructor can’t initialize data members in the base-class. That’s the job of the base-class’ constructor. Oh dear.
Calling the Base-Class Constructor If the base-class has a default constructor, we are in luck: that can be used. If not… We have to somehow “call” the base-class constructor from the derived-class’ constructor. Oh dear!
Calling the Base-Class Constructor The initialization list to the rescue! We put the base-class’ constructor “call” in the initialization list. The initialization list code happens before the { } of the constructor so the base-class will be completely set up before any specializations take place.
Calling the Base-Class Constructor Suppose, for the Question class, a constructor taking a String for the text had been written: then the ChoiceQuestion class constructor would have to take a String parameter to use to set Question’s text and call its constructor in the initialization list... …passing the data for it’s text data member. class Question { public: Question(String question_text); ... ChoiceQuestion::ChoiceQuestion(string question_text) : Question(question_text) }
Calling the Base-Class Constructor
Overriding Member Functions Recall that our design requires that the display member function be rewritten in the ChoiceQuestion class. This is called overriding a member function.
Overriding Member Functions ChoiceQuestion’s display method needs to: Display the question text. Display the answer choices.
Overriding Member Functions The second part is easy – just a data member int ChoiceQuestion::display() const { // Display the question text ... // Display the answer choices for (int i = 0; i < choices.size(); i++) cout << i + 1 << ": " << choices[i] << endl; }
Overriding Member Functions The first part seems easy too – call the display in Question: int ChoiceQuestion::display() const { // Display the question text display(); // Display the answer choices ...
Overriding Member Functions Oh no! this points to me – and I have a display member function! int ChoiceQuestion::display() const { // Display the question text display(); // Display the answer choices ... implicit parameter.display();
Overriding Member Functions I’m talking to myself!!! int ChoiceQuestion::display() const { // Display the question text display(); // Display the answer choices ... } implicit parameter.display();
Overriding Member Functions The solution is to do what you said in the first place: Call Question’s display: int ChoiceQuestion::display() const { // Display the question text display(); // Display the answer choices ... }
Overriding Member Functions The solution is to do what you said in the first place: Call Question’s display: int ChoiceQuestion::display() const { // Display the question text Question::display(); // Display the answer choices ... }
Overriding Member Functions When you override a function, you usually want to extend the functionality of the base-class version. Therefore, you often need to invoke the base-class version before extending it.
Overriding Member Functions Use the BaseClass::function notation.
Overriding Member Functions However, you have no obligation to call the base-class function. Occasionally, a derived class overrides a base-class function and specifies an entirely different functionality.
A Program With ChoiceQuestion In the following program, the definition of the Question class, which you have already seen, is placed into question.h, and the implementation is in question.cpp.
A Program With ChoiceQuestion #include <iostream> #include <sstream> #include <vector> #include "question.h" class ChoiceQuestion : public Question { public: /** Constructs a choice question with no choices. */ ChoiceQuestion(); ch10/quiz2/test.cpp
A Program With ChoiceQuestion /** Adds an answer choice to this question. @param choice the choice to add @param correct true if this is the correct choice, false otherwise */ void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; }; ChoiceQuestion::ChoiceQuestion() { } ch10/quiz2/test.cpp
A Program With ChoiceQuestion void ChoiceQuestion::add_choice(string choice, bool correct) { choices.push_back(choice); if (correct) // Convert choices.size() to string ostringstream stream; stream << choices.size(); string num_str = stream.str(); set_answer(num_str); } ch10/quiz2/test.cpp
A Program With ChoiceQuestion void ChoiceQuestion::display() const { // Display the question text Question::display(); // Display the answer choices for (int i = 0; i < choices.size(); i++) cout << i + 1 << ": " << choices[i] << endl; } ch10/quiz2/test.cpp
A Program With ChoiceQuestion int main() { string response; cout << boolalpha; // Ask a basic question Question q1; q1.set_text("Who was the inventor of C++?"); q1.set_answer("Bjarne Stroustrup"); q1.display(); cout << "Your answer: "; getline(cin, response); cout << q1.check_answer(response) << endl; ch10/quiz2/test.cpp
A Program With ChoiceQuestion // Ask a choice question ChoiceQuestion q2; q2.set_text("In which country was the inventor of C++ born?"); q2.add_choice("Australia", false); q2.add_choice("Denmark", true); q2.add_choice("Korea", false); q2.add_choice("United States", false); q2.display(); cout << "Your answer: "; getline(cin, response); cout << q2.check_answer(response) << endl; return 0; } ch10/quiz2/test.cpp
Common Error: Forgetting the Base-Class Name Don’t forget to use the BaseClass::memberFunction notation when you want to call a member function in your base class – especially when you are writing: DerivedClass::memberFunction (Yes, Mom.)
Common Error: Forgetting the Base-Class Name Manager derives from Employee which has a method named get_salary. Careful: double Manager::get_salary() const { double base_salary = get_salary(); // Error—should be Employee::get_salary() return base_salary + bonus; }
The Slicing Problem Did someone say slice?
It would be nicer if all questions were collected in an array. The Slicing Problem In the main function of that last program, there was some repetitive code to display each question and check the responses. It would be nicer if all questions were collected in an array. You could then loop to present them to the user:
because ChoiceQuestion is a special case of Question The Slicing Problem const int QUIZZES = 2; Question quiz[QUIZZES]; quiz[0].set_text("Who was the inventor of C++?"); quiz[0].set_answer("Bjarne Stroustrup"); ChoiceQuestion cq; cq.set_text("In which country was the inventor of C++ born?"); cq.add_choice("Australia", false); ... quiz[1] = cq; for (int i = 0; i < QUIZZES; i++) { quiz[i].display(); cout << "Your answer: "; getline(cin, response); cout << quiz[i].check_answer(response) << endl; } This works because ChoiceQuestion is a special case of Question
However, is it really working? Here’s a run of the program: The Slicing Problem However, is it really working? Here’s a run of the program: Who was the inventor of C++? Your answer: Bjarne Stroustrup true In which country was the inventor of C++ born? Your answer: Where are the choices?
When you assign a derived class to a base class memory location, The Slicing Problem When you assign a derived class to a base class memory location, Slicing Happens! Ah! sharp knives…
The assignment copies text The Slicing Problem The assignment copies text and answer just fine.
But where does choices go? The Slicing Problem But where does choices go?
But where does choices go? The Slicing Problem But where does choices go?
The Slicing Problem
The Slicing Problem
The Slicing Problem Sliced off…
The Slicing Problem
The Slicing Problem
The Slicing Problem SLICED OFF!
The Slicing Problem …and into the stewpot!
The assignment copies only the base portion. The Slicing Problem The assignment copies only the base portion.
Pointers to Base and Derived Classes To access objects from different classes in a class hierarchy, use pointers (to avoid slicing). I’m putting the knife away… …forever
Pointers to Base and Derived Classes To access objects from different classes in a class hierarchy, use pointers (to avoid slicing). (pointers are sharp… hmm…)
Pointers to Base and Derived Classes Pointers to the various objects all have the same size: the size of a memory address. (convenient)
Pointers to Base and Derived Classes Pointers to base classes can hold pointers to ANY object publicly derived from it – as far down the inheritance chain as you want to go. (very general)
Pointers to Base and Derived Classes The opposite will not work: Assigning a base pointer to a derived pointer location will generate a compiler error. (pointer well taken) (make that: point well taken)
Pointers to base and Derived Classes To manage a set of ANY Questions including any IS-A Questions: Fill-in-the-blank Numeric Free response Choice (single)
Pointers to base and Derived Classes To manage a set of ANY Questions and Questions inheriting from IS-A Questions Fill-in-the-blank Numeric Free response Choice (single) Choice (multiple – inherits from Choice (single)
Pointers to base and Derived Classes … and Questions that are just a gleam in a programmer’s eye Fill-in-the-blank Numeric Free response Choice (single) Choice (multiple – inherits from Choice (single) Essay Essay written longhand in pomegranate juice
Pointers to base and Derived Classes To manage all of these, use a vector<Question*> of an array of Question* and store only pointers to the different kinds of Questions Fill-in-the-blank Numeric Free response Choice (single) Choice (multiple – inherits from Choice (single) Essay Essay written longhand in pomegranate juice
Code for Pointers to base and Derived Classes Notice the use of new and -> : Question* quiz[2]; quiz[0] = new Question; quiz[0] -> set_text("Who was the inventor of C++?"); quiz[0] -> set_answer("Bjarne Stroustrup"); ChoiceQuestion* cq_pointer = new ChoiceQuestion; cq_pointer -> set_text("In which country… …C++ born?"); cq_pointer -> add_choice("Australia", false); ... quiz[1] = cq_pointer;
Code for Pointers to base and Derived Classes And the assignment of a derived-class pointer to a base: Question* quiz[2]; quiz[0] = new Question; quiz[0] -> set_text("Who was the inventor of C++?"); quiz[0] -> set_answer("Bjarne Stroustrup"); ChoiceQuestion* cq_pointer = new ChoiceQuestion; cq_pointer -> set_text("In which country… …C++ born?"); cq_pointer -> add_choice("Australia", false); ... quiz[1] = cq_pointer;
Vector of Pointers to Base to Manage Base and Derived Question* quiz[2]; quiz[0] = new Question; quiz[1] = new ChoiceQuestion;
Using vector<Base*> The code to present all questions – any kind of Question – is: for (int i = 0; i < QUIZZES; i++) { quiz[i] -> display(); cout << "Your answer: "; getline(cin, response); cout << quiz[i] -> check_answer(response) << endl; }
When you call the display member function on a Virtual Functions (take a deep breath) When you call the display member function on a Question* pointer that currently contains a pointer to a ChoiceQuestion, you want the choices to be displayed, right? (Whew … yes.)
But that’s not what happens. Virtual Functions But that’s not what happens. (Oh dear)
quiz[i]->display(); Virtual Functions For reasons of efficiency, by default, the call quiz[i]->display(); always calls Question::display because the type of quiz[i] is Question*. (Do we give up hope now and go home?)
that the function should be the one in the thing pointed to. Virtual Functions In C++, you must alert the compiler that the function call needs to not be the default, that the function should be the one in the thing pointed to. (How?) You use the virtual reserved word for this purpose.
Notice where virtual is written. Virtual Functions Notice where virtual is written. The virtual reserved word must be used in the base class. class Question { public: Question(); void set_text(string question_text); void set_answer(string correct_response); virtual bool check_answer(string response) const; virtual void display() const; private: ... };
All functions with the same name and parameter types Virtual Functions All functions with the same name and parameter types in derived classes are then automatically virtual.
Virtual Functions Although not needed, it is considered good practice to write the virtual reserved word in the derived-class functions. class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); virtual void display() const; private: ... };
You do not write virtual in the function definition: Virtual Functions You do not write virtual in the function definition: void Question::display() const { cout << text << endl; }
what you call is what you get! Virtual Functions With virtual member functions, what you call is what you get! (Got it!) quiz[i]-> display();
The quiz vector collects a mixture of all kinds of Questions. Polymorphism The quiz vector collects a mixture of all kinds of Questions. Such a collection is called polymorphic (literally, “of multiple shapes”).
Inheritance is used to express this commonality. Polymorphism Objects in a polymorphic collection have some commonality but are not necessarily of the same type. Inheritance is used to express this commonality. virtual functions enable variations in behavior.
Virtual functions give programs a great deal of flexibility. Polymorphism Virtual functions give programs a great deal of flexibility. The question presentation loop describes only the general mechanism: “Display the question, get a response, and check it”.
“Display the question” Polymorphism Each object knows on its own how to carry out its specific version of these general tasks: “Display the question” (my way) and “Check a response” (my way).
Using virtual functions makes programs easily extensible. Polymorphism Using virtual functions makes programs easily extensible.
Suppose we want to have a new Polymorphism Suppose we want to have a new kind of question for calculations, where we are willing to accept an approximate answer. All we need to do is to define a new class NumericQuestion, with its own check_answer function.
and these new numeric questions. Polymorphism Then we can populate any quiz vector with a mixture of plain questions, choice questions, and these new numeric questions. They will fit in just fine because: they IS-A Questions. (That’s so nice.)
The code that presents the questions need not be changed at all! Polymorphism The code that presents the questions need not be changed at all! The calls to the virtual functions automatically select the correct member functions of the newly defined classes.
The Complete Program with Polymorphism #ifndef QUESTION_H #define QUESTION_H #include <string> using namespace std; class Question { public: /** Constructs a question with empty question and answer. */ Question(); @param question_text the text of this question void set_text(string question_text); @param correct_response the answer for this question void set_answer(string correct_response);
The Complete Program with Polymorphism /** @param response the response to check @return true if the response was correct, false otherwise */ virtual bool check_answer(string response) const; Displays this question. virtual void display() const; private: string text; string answer; }; #endif
The Complete Program with Polymorphism #ifndef CHOICEQUESTION_H #define CHOICEQUESTION_H #include <vector> #include "question.h" class ChoiceQuestion : public Question { public: /** Constructs a choice question with no choices. */ ChoiceQuestion(); Adds an answer choice to this question. @param choice the choice to add @param correct true if this is the correct choice, false otherwise void add_choice(string choice, bool correct);
The Complete Program with Polymorphism virtual void display() const; private: vector<string> choices; }; #endif
The Complete Program with Polymorphism #include <iostream> #include "question.h" #include "choicequestion.h" int main() { string response; cout << boolalpha; // Make a quiz with two questions const int QUIZZES = 2; Question* quiz[QUIZZES]; quiz[0] = new Question; quiz[0]->set_text("Who was the inventor of C++?"); quiz[0]->set_answer("Bjarne Stroustrup");
The Complete Program with Polymorphism ChoiceQuestion* cq_pointer = new ChoiceQuestion; cq_pointer->set_text("In which country… …C++ born?"); cq_pointer->add_choice("Australia", false); cq_pointer->add_choice("Denmark", true); cq_pointer->add_choice("Korea", false); cq_pointer->add_choice("United States", false); quiz[1] = cq_pointer; // Check answers for all questions for (int i = 0; i < QUIZZES; i++) { quiz[i]->display(); cout << "Your answer: "; getline(cin, response); cout << quiz[i]->check_answer(response) << endl; } return 0;
CHAPTER SUMMARY
CHAPTER SUMMARY