Chapter 10 Inheritance CSC 252 C++

Réussis tes devoirs et examens dès maintenant avec Quizwiz!

Question class and a simple test program. Note that the Question class constructor needs to do no work because the text and answer data members are automatically set to the empty string. The boolalpha stream manipulator in the main function causes Boolean values to be displayed as true and false instead of the default 1 and 0.

#include <iostream> #include <sstream> #include <string> using namespace std; class Question { public: /** Constructs a question with empty text 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); /** @param response the response to check @return true if the response was correct, false otherwise */ bool check_answer(string response) const; /** Displays this question. */ void display() const; private: string text; string answer; }; Question::Question() { } void Question::set_text(string question_text) { text = question_text; } void Question::set_answer(string correct_response) { answer = correct_response; } bool Question::check_answer(string response) const { return response == answer; } void Question::display() const {

void process_input(istream& in); You can call this function withan ifstream object or with an istream object. Why?

Because istream is more general than ifstream. void process_input(ifstream& in); This works by inheritance:

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. Decide which functions are overridden in derived classes.

For each derived class and each of the common responsibilities, decide whether the behavior can be inherited or whether it needs to be overridden. Declare any functions that are overridden as virtual in the root of the hierarchy. Getting the balance is common to all account types. Withdrawing and end of month processing are different for the derived classes, so they need to be declared virtual. Because it is entirely possible that some future account type will levy a fee for deposits, it seems prudent to declare the deposit member function virtual as well. class BankAccount { public: virtual void deposit(double amount); virtual void withdraw(double amount); virtual void month_end(); double get_balance() const; private: . . . };

After specifying the class you are inheriting from,you only write_____

the differences:

When you invoke a virtual function on an object, the C++ run-time system determines which________ function to call, depending on the ____ to which the ______belongs.

actual member class object

An _________(an input stream that reads from a file) is a special case of an_____ (an input stream that reads data from any source). If you have an________, it can be the argument for a function that expects an i_________.

ifstream istream ifstream istream

In our example program, we used the default constructor of the base class. However, if a base class has no default constructor, you must use the____________

initializer list syntax.

Suppose you have designed an inheritance hierarchy that includes the following relationships: Guitar is derived from Instrument AcousticGuitar is derived from Guitar ElectricGuitar is derived from Guitar Given the declarations below, which of the objects CANNOT be passed to the function tune(Guitar& g)? AcousticGuitar ag; ElectricGuitar eg; Guitar my_guitar; Instrument my_instrument;

Instrument my_instrument;

What are the developing an Inheritance Hierarchy

Problem Statement Step 1List the classes that are part of the hierarchy. Step 2Organize the classes into an inheritance hierarchy. Step 3Determine the common responsibilities. Step 4Decide which functions are overridden in derived classes. Step 5Define the public interface of each derived class. Step 6Identify data members. Step 7Implement constructors and member functions. Step 8Allocate objects on the free store and process them.

Figure 4 shows the layout of a ChoiceQuestion object. It inherits the text and answer data members from the_____ base object, and it adds an additional data member: ________

Question the choices vector.

Override

Redefine a function from a base class in a derived class.

uppose you are to design an inheritance hierarchy with the following classes: Snake, Crocodile, Reptile, and Turtle. Which of these is the base class? Snake Turtle Reptile Crocodile

Reptile

Write a question class

// sec01/demo.cpp #include <iostream> #include <sstream> #include <string> using namespace std; class Question { public: /** Constructs a question with empty text and answer. */ Question(); /** @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);

The ____ symbol denotes inheritance.

:

Base class

A class from which another class is derived.

Derived class

A class that modifies a base class by adding data members, adding member functions, or redefining member functions. The derived class inherits data and behavior from the base class.

Virtual function

A function that can be redefined in a derived class. The actual function called depends on the type of the object on which it is invoked at run time.

All Cars are Vehicles. All Motorcycles are Vehicles. All Sedans are Vehicles. Which class is the base class and what derives from what

All Cars are Vehicles. All Motorcycles are Vehicles. All Sedans are Vehicles. Vehicles is the base class. Car is a derived class. Truck derives from Vehicle

Cars share the common traits of all vehicles, such as the ability to transport people from one place to another. We say that the class Car _____from the class Vehicle. In this relationship, the Vehicle class is the base class and the Car class is the derived class.

Cars share the common traits of all vehicles, such as the ability to transport people from one place to another. We say that the class Car inherits from the class Vehicle. In this relationship, the Vehicle class is the base class and the Car class is the derived class.

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. How would we Organize the classes into an inheritance hierarchy?

Draw a UML diagram that shows base and derived classes. Here is the diagram for our example:

Everything about being a _______ is inherited by ________ and ______ and _______.

Everything about being a Vehicle is inherited by Cars and Trucks and SUVs.

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. Step 8Allocate objects on the free store and process them.

For polymorphism (that is, variation of behavior) to work in C++, you need to call virtual functions through pointers. The easiest strategy is to allocate all polymorphic objects on the free store, using the new operator. In our sample program, we allocate 5 checking accounts and 5 savings accounts and store their addresses in an array of bank account pointers. Then we accept user commands and execute deposits, withdrawals, and monthly processing. int main() { cout << fixed << setprecision(2); // Create accounts const int ACCOUNTS_SIZE = 10; BankAccount* accounts[ACCOUNTS_SIZE]; for (int i = 0; i < ACCOUNTS_SIZE / 2; i++) { accounts[i] = new CheckingAccount; } for (int i = ACCOUNTS_SIZE / 2; i < ACCOUNTS_SIZE; i++) { SavingsAccount* account = new SavingsAccount; account->set_interest_rate(0.75); accounts[i] = account; } // Execute commands bool more = true; while (more) { cout << "D)eposit W)ithd

What goes where? public New and changed member functions Additional data members Here is the syntax for the definition of a derived class: class ChoiceQuestion :_____ Question { public: _________ private: __________ };

Here is the syntax for the definition of a derived class: class ChoiceQuestion : public Question { public: New and changed member functions private: Additional data members };

Suppose we add the following function to the Question class: void Question::ask() const { display(); cout << "Your answer: "; getline(cin, response); cout << check_answer(response) << endl; } Now consider the call ChoiceQuestion cq; cq.set_text("In which country was the inventor of C++ born?"); . . . cq.ask(); Which display and check_answer function will the ask function call?

If you look inside the code of the Question::ask function, you can see that these functions are executed on the implicit parameter: void Question::ask() const { implicit parameter.display(); cout << "Your answer: "; getline(cin, response); cout << implicit parameter.check_answer(response) << endl; } The implicit parameter in our call is cq, an object of type ChoiceQuestion. Because the display and check_answer functions are virtual, the ChoiceQuestion versions of the functions are called automatically. This happens even though the ask function is defined in the Question class, which has no knowledge of the ChoiceQuestion class. As you can see, virtual functions are a very powerful mechanism. The Question class supplies an ask function that specifies the common nature of asking a question, namely to display it and check the response. How the displaying and checking are carried out is left to the derived classes.

In object-oriented design, __________ is a relationship between a more general class (called the _________) and a more specialized class (called the __________ class).

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).

As an example, we will consider a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. •A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. What would be Step 1List the classes that are part of the hierarchy?

In our case, the problem description yields two classes: SavingsAccount and CheckingAccount. To express the commonality between them, we will introduce a class BankAccount.

Informally, the inheritance relationship is called the ____ relationship. Contrast this relationship with the _________ relationship that we discussed in Section 9.8. Every car is a vehicle. Every vehicle has an engine.

Informally, the inheritance relationship is called the is-a relationship. Contrast this relationship with the has-a relationship that we discussed in Section 9.8. Every car is a vehicle. Every vehicle has an engine.

___ is a relationship between a more general and a more specific class.

Inheritance The more specific class inherits behavior from the more general class.

What will happen if public is not present before Question class ChoiceQuestion : Question // Error

It is a common error to forget the reserved word public that must follow the colon after the derived-class name. class ChoiceQuestion : Question // Error { . . . }; The class definition will compile. The ChoiceQuestion still inherits from Question, but it inherits privately. That is, only the member functions of ChoiceQuestion get to call member functions of Question. Whenever another function invokes a Question member function on a ChoiceQuestion object, the compiler will flag this as an error: int main() { ChoiceQuestion q; . . . cout << q.check_answer(response); // Error } This private inheritance is rarely useful. In fact, it violates the spirit of using inheritance in the first place—namely, to create objects that are usable just like the base-class objects. You should always use public inheritance and remember to supply the public reserved word in the definition of the derived class.

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. Identify data members.

List the data members for each class. If you find a data member that is common to all classes, be sure to place it in the base of the hierarchy. All accounts have a balance. We store that value in the BankAccount base class: class BankAccount { . . . private: double balance; }; The SavingsAccount class needs to store the interest rate. It also needs to store the minimum monthly balance, which must be updated by all withdrawals: class SavingsAccount : public BankAccount { . . . private: double interest_rate; double min_balance; }; The CheckingAccount class needs to count the withdrawals, so that the charge can be applied after the free withdrawal limit is reached: class CheckingAccount : public BankAccount { . . . private: int withdrawals; };

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. Define the public interface of each derived class.

List the derived classes responsibilities other than those of the base class & member functions to be overridden. Specify how the objects of the derived classes should be constructed. In this example, find a way of setting the interest rate for the savings and specify constructors and overridden functions. class SavingsAccount : public BankAccount { public: /** Constructs a savings account with a zero balance. */ SavingsAccount(); /** Sets the interest rate for this account. @param rate the monthly interest rate in percent */ void set_interest_rate(double rate); virtual void withdraw(double amount); virtual void month_end(); private: . . . }; class CheckingAccount : public BankAccount { public: /** Constructs a checking account with a zero balance. */ CheckingAccount(); virtual void withdraw(double amount); virtual void month_end(); private: . . . };

Now for those different kinds of questions. Each of the different kinds of questions IS-A Question so we code by starting with the _______and then we write code for what makes the __________.

Now for those different kinds of questions. Each of the different kinds of questions IS-A Question so we code by starting with the base class (Question)and then we write code for what makes the different typesspecial versions of more general Question type.

Do objects of polymorphism always have the same type?

Objects in a polymorphic collection have some commonality but are not necessarily of the same type Inheritance is used to express this commonality, and virtual functions enable variations in behavior.

Polymorphism

Selecting a function among several functions that have the same name on the basis of the actual type of the implicit parameter.

Slicing

Slicing an object Copying an object of a derived class into a variable of the base class, thereby losing the derived-class data.

Some programmers build inheritance hierarchies in which each object has a tag that indicates its type, commonly a string. They then query that string: if (q->get_type() == "Question") { } else if (q->get_type() == "ChoiceQuestion") { } What is a better alternative?

Some programmers build inheritance hierarchies in which each object has a tag that indicates its type, commonly a string. They then query that string: if (q->get_type() == "Question") { // Do something } else if (q->get_type() == "ChoiceQuestion") { // Do something else } This is a poor strategy. If a new class is added, then all these queries need to be revised. In contrast, consider the addition of a class NumericQuestion to our quiz program. Nothing needs to change in that program because it uses virtual functions, not type tags. Whenever you find yourself adding a type tag to a hierarchy of classes, reconsider and use virtual functions instead.

Using virtual functions makes programs easily extensible. What does this mean?

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. Then we can populate the quiz array with a mixture of plain questions, choice questions, and numeric questions. 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.

Why provide a function that processes istream objects instead of ifstream objects?

That function is more useful because it can handle any kind of input stream (such as an istringstream, which is convenient for testing). This again is the substitution principle at work.

Inheritance

The "is-a" relationship between a general base class and a specialized derived class.

Here is the code to set up the array of pointers (see Figure 6): 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 was the inventor of C++ born?"); cq_pointer->add_choice("Australia", false); . . . quiz[1] = cq_pointer; Write the code to present all questions.

The code to present all questions is for (int i = 0; i < QUIZZES; i++) { quiz[i]->display(); cout << "Your answer: "; getline(cin, response); cout << quiz[i]->check_answer(response) << endl; }

The derived class inherits _____ data members and ____ functions that it does not override. some all none few

The derived class inherits all data members and all functions that it does not override.

The derived class inherits the ______________ from the base class.

The derived class inherits the member functions from the base class.

The derived class inherits the member functions from the base class. If you are not satisfied with the behavior of the inherited member function, you can _______________________________________.

The derived class inherits the member functions from the base class. If you are not satisfied with the behavior of the inherited member function, you can override it by specifying a new implementation in the derived class.

The ______-class constructor calls the ______-class constructor before executing the code inside the ______.

The derived-class constructor calls the base-class constructor before executing the code inside the { }.

It is a common error to accidentally provide an overloaded function when you actually mean to override a function. Consider this scary example: class ChoiceQuestion : public Question { public: void display(); . . . }; Why might this not work?

The display member function in the Question class has subtly different parameter types: the this pointer that points to the implicit parameter (see Section 9.10.3) has type const Question*, whereas in the ChoiceQuestion class, the this pointer is not const class ChoiceQuestion : public Question { public: void display(); // Does not override Question::display() const . . . };

The inheritance relationship is very powerful because it allows us to reuse ______ with objects of _____ classes.

The inheritance relationship is very powerful because it allows us to reuse algorithms with objects of different classes.

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. Step 7 Implement constructors and member functions.

The member functions of the BankAccount class update or return the balance: BankAccount::BankAccount() { balance = 0; } void BankAccount::deposit(double amount) { balance = balance + amount; } void BankAccount::withdraw(double amount) { balance = balance - amount; } double BankAccount::get_balance() const { return balance; } At the level of the BankAccount base class, we can say nothing about end of month processing. We choose to make that function do nothing: void BankAccount::month_end() { } In the withdraw member function of the SavingsAccount class, the minimum balance is updated. Note the call to the base-class member function: void SavingsAccount::withdraw(double amount) { BankAccount::withdraw(amount); double balance = get_balance(); if (balance < min_balance) { min_balance = balance; } } In the month_end member function of the SavingsAccount class, the interest is deposited into the account. We must call the deposit member function bec

Substitution principle

The principle that a derived-class object can be used in place of any base-class object.

What will be the problem statement for this example, we will consider a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. •A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal.

The program will manage a set of accounts of both types, and it should be structured so that other account types can be added without affecting the main processing loop. Supply a menu D)eposit W)ithdraw M)onth end Q)uit For deposits and withdrawals, query the account number and amount. Print the balance of the account after each transaction. In the "Month end" command, accumulate interest or clear the transaction counter, depending on the type of the bank account. Then print the balance of all accounts.

If you have a hybrid car should you make a hybrid class for hybrids to log refueling and/or repairs

The purpose of inheritance is to model objects with different behavior. When students first learn about inheritance, they have a tendency to overuse it, by creating multiple classes even though the variation could be expressed with a simple data member. Consider a program that tracks the fuel efficiency of a fleet of cars by logging the distance traveled and the refueling amounts. Some cars in the fleet are hybrids. Should you create a derived class HybridCar? Not in this application. Hybrids don't behave any differently than other cars when it comes to driving and refueling. They just have a better fuel efficiency. A single Car class with a data member double miles_per_gallon; is entirely sufficient. However, if you write a program that shows how to repair different kinds of vehicles, then it makes sense to have a separate class HybridCar. When it comes to repairs, hybrid cars behave differently from other cars.

You can't just access the answer member in the base class. Fortunately, the Question class has a set_answer member function. You can call that member function. On which object?

The question that you are currently modifying—that is, the implicit parameter of the ChoiceQuestion::add_choice function.

Consider the display function of the ChoiceQuestion class. It needs to override the base-class display function in order to show the choices for the answer. Specifically, the derived-class function needs to •Display the question text. •Display the answer choices. Write code to show the answer choices

The second part is easy because the answer choices are a data member of the derived class. void 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; } }

The ______________ states that you can always use a derived-class object when a base-class object is expected. Suppose we have an algorithm or function that manipulates a Vehicle object. Since a car IS-A vehicle will algorithms from the vehicle class affect cars?

The substitution principle statesthat you can always use a derived-class objectwhen a base-class object is expected. Suppose we have an algorithm or function thatmanipulates a Vehicle object. Since a car IS-A vehicle, we can supplya Car object to such an algorithm or function,and it will work correctly.

ChoiceQuestion object has three data members, whereas a Question object has just two. What happens?

There is no room to store the derived-class data. That data simply gets sliced away when you assign a derived-class object to a base-class variable (see Figure 5) This problem is very typical of code that needs to manipulate objects from a mixture of classes in an inheritance hierarchy. Derived-class objects are usually bigger than base-class objects, and objects of different derived classes have different sizes. An array of objects cannot deal with this variation in sizes. Instead, you need to store the actual objects elsewhere and collect their locations in an array by storing pointers.

In C++, two functions can have the same name, provided they differ in their parameter types. For example, you can define two member functions called display in the Question class: class Question { public: virtual void display() const; virtual void display(ostream& out) const; . . . }; What would we call this situation?

These are different functions, each with its own implementation. The C++ compiler considers them to be completely unrelated. We say that the display name is overloaded. This is different from overriding, where a derived class function provides an implementation of a base class function with the same name and the same parameter types.

Those things specific to Cars are only inherited by _____ and _____.

Those things specific to Cars are only inherited by Sedans and SUVs.

You can call the inherited member functions or the data members on a derived-class object: choice_question.set_answer("2");

You can call the inherited member functions on a derived-class object: However, the inherited data members are inaccessible. Because these members are private data of the base class, only the base class has access to them. The derived class has no more access rights than any other class.n particular, the ChoiceQuestion member functions cannot directly access the answer member. These member functions must use the public interface of the Question class to access its private data, just like every other function.

The slicing problem commonly occurs when _________

a function has a polymorphic parameter

The more general class is called the ___ class.

base The base class is the one that contains the more general features. A Car is special kind of Vehicle, so Vehicle would be the base class in this example.

In order to specify another constructor, you use an initializer list, as described in Special Topic 9.2. Specify the name of the __________and _______

base class and the construction arguments in the initializer list. For example, suppose the Question base class had a constructor for setting the question text. Here is how a derived-class constructor could call that base-class constructor: ChoiceQuestion::ChoiceQuestion(string question_text) : Question(question_text) {

When you collect objects of different classes in a class hierarchy, and then invoke a member function, you want the appropriate member function to be applied. For example, when you call the display member function on a Question* pointer that happens to point to a ChoiceQuestion, you want the choices to be displayed. For reasons of efficiency, this is not the default in C++. By default, a call quiz[i]->display(); because of this call

call Question::display because the type of quiz[i] is Question*.

A derived class has no access to the data members of the base class. ChoiceQuestion::ChoiceQuestion(string question_text) { text = question_text; // Error—tries to access private base-class member } When faced with a compiler error, beginners commonly "solve" this issue by adding another data member with the same name to the derived class: What happens?

class ChoiceQuestion : public Question { . . . private: vector<string> choices; string text; // Don't! } Sure, now the constructor compiles, but it doesn't set the correct text! Such a ChoiceQuestion object has two data members, both named text. The constructor sets one of them, and the display member function displays the other. Instead of uselessly replicating a base-class data member, you need to call a member function that updates the base-class member, such as the set_text function in our example.

Through inheritance, each of the derived classes has the data members and member functions set up in class Question. - plus "specialness" which is not inherited, but added in the definition of each derived class Where do new, additonal, and changed member functions go. What symbol denotes iheritance?

class ChoiceQuestion : public Question { public: // New and changed member // functions will go here private: // Additional data members // will go here }; The : denotes interitance

to determine the actual type of the object to which quiz[i] points, which can be either a Question or a ChoiceQuestion object, and then call the appropriate function. In C++, you must alert the compiler that the function call needs to be preceded by the appropriate function selection, which can be a different one for every iteration in the loop. You use the virtual reserved word for this purpose: Write code to show this

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: . . . };

Write a base class for question We want a object of Question type to work like this: 1. First, the programmer sets the question textand the correct answer in the Question object. ● 2.When a user takes the test, the programmerasks the Question to display the text of the question ● 3.The program gets the use's response and passes it to the Question object for evaluation, to display true or false.

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; };

Consider the process of constructing a derived-class object. A derived-class constructor can only initialize the data members of the derived class. But the base-class data members also need to be initialized. Unless you specify otherwise, the base-class data members are initialized with the____________

default constructor of the base class.

Unless specified otherwise, the base-class data members are initialized with the __________

default constructor.

The reserved word_________ is required for a technical reason (see Common Error 10.1).

public

In C++, you form a_1________ class from a__2______ class by specifying what makes the __3____ class different. You define the__4____ functions that are new to the derived class. The _5______ class inherits all ___6______ from the ___7____ class, but you can change the ____8________ if the inherited behavior is not appropriate.

derived base derived member derived member functions base implementation The derived class automatically inherits all data members from the base class. You only define the added data members.

The more specialized class is called the ___ class.

derived A Car is a specialized kind of Vehicle, so the Car class would be derived from Vehicle.

Which pointer is legal? From a base-class pointer to a derived-class pointer or from a a derived-class pointer to a base-class pointer?

from a base-class pointer to a derived-class pointer—is an error.

You do not supply the reserved word virtual in the _______

function definition:

A common error in extending the functionality of a base-class function is to forget the base-class name. For example, to compute the salary of a manager, we should double Manager::get_salary() const { double base_salary = get_salary(); return base_salary + bonus; }

here get_salary() refers to the get_salary function applied to the implicit parameter of the member function. The implicit parameter is of type Manager, and there is a Manager::get_salary function, so that function is called. Of course, that is a recursive call to the function that we are writing. Instead, you must specify which get_salary function you want to call. In this case, you need to call Employee::get_salary explicitly. Whenever you call a base-class function from a derived-class function with the same name, be sure to give the full name of the function, including the base-class name.

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 was the inventor of C++ born?"); cq_pointer->add_choice("Australia", false); . . . quiz[1] = cq_pointer; the last assignment assigns a derived-class pointer of type ChoiceQuestion* to a base-class pointer of type Question*. Is this allowed?

his is perfectly legal. A pointer is the starting address of an object. Because every ChoiceQuestion is a special case of a Question, the starting address of a ChoiceQuestion object is, in particular, the starting address of a Question object. The reverse assignment—from a base-class pointer to a derived-class pointer—is an error.

What can we do to fix the issue class Question { public: virtual void display() const; virtual void display(ostream& out) const; . . . }; class ChoiceQuestion : public Question { public: void display(); . . . };

n C++ 11, you can use the reserved word override to tag any member function that should override a virtual function: class ChoiceQuestion : public Question { public: void display() override; . . . }; If the member function does not actually override a virtual function, the compiler generates an error. This is good, because you can then have a closer look and add the missing const reserved word. The compiler also generates an error if you forget to declare the base class member function as virtual. If you use C++ 11, it is a good idea to take advantage of the override reserved word.

In C++ it is legal to copy a derived-class object into a base-class variable. What are some problems that can occur from this?

n C++ it is legal to copy a derived-class object into a base-class variable. However, any derived-class information is lost in the process. For example, when a Manager object is assigned to a variable of type Employee, the result is only the employee portion of the manager data: Manager m; . . . Employee e = m; // Holds only the Employee base data of m Any information that is particular to managers is sliced off, because it would not fit into a variable of type Employee. To avoid slicing, you can use pointers.

to hold pointers, allocate all objects by calling _____, and use the _______ operator

new, ->instead of the dot operator.

The add_choice function is specific to the ChoiceQuestion class. You can only apply it to ChoiceQuestion objects, not general Question objects. However, the display function is a redefinition of a function that exists in the base class; it is redefined to take into account the special needs of the derived class. We say that the derived class______ this function.

overrides

To access objects from different classes in a class hierarchy, use ________

pointers. Pointers to the various objects all have the same size—namely, the size of a memory address—even though the objects themselves may have different sizes.

The quiz array collects a mixture of both kinds of questions. Such a collection is called

polymorphic (literally, "of multiple shapes").

Whenever a virtual function is called, the compiler determines the type of the implicit parameter in the particular call at run time. The appropriate function for that object is then called. For example, when the display function is declared virtual, the call

quiz[i]->display(); always calls the function belonging to the actual type of the object to which quiz[i] points—either Question::display or ChoiceQuestion::display.

As you saw in Chapter 9, if you invoke a member function on the implicit parameter, you don't specify the parameter but just write the member function name as:______

set_answer(num_str); The compiler interprets this call as implicit parameter.set_answer(num_str);

When an algorithm expects an object from the base class (e.g., Vehicle), you can supply a derived class object (e.g. Car). This is called the ___ principle.

substitution You can always use (substitute) a derived class object when a base class object is expected.

uppose we have an algorithm that manipulates a Vehicle object. Because a car is a special kind of vehicle, we can supply a Car object to such an algorithm, and it will work correctly. This is an example of the_________

substitution principle

For a bank that offers its customers the following account types: •A savings account that earns interest. The interest compounds monthly and is computed on the minimum monthly balance. A checking account that has no interest, gives you three free withdrawals per month, and charges a $1 transaction fee for each additional withdrawal. Determine the common responsibilities.

tep 3Determine the common responsibilities. In Step 2, you will have identified a class at the base of the hierarchy. That class needs to have sufficient responsibilities to carry out the tasks at hand. To find out what those tasks are, write pseudocode for processing the objects: For each user command If it is a deposit or withdrawal Deposit or withdraw the amount from the specified account. Print the balance. If it is month end processing For each account Call month end processing. Print the balance. From the pseudocode, we obtain the following list of common responsibilities that every bank account must carry out: Deposit money. Withdraw money. Get the balance. Carry out month end processing.

When you override a function, you usually want to extend the functionality of the base-class version. Therefore, you often need to invoke_________. To invoke it, you need to _________________________.

the base-class version before extending it. use the BaseClass::function notation. 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.

What is the outcome of this code void ChoiceQuestion::display() const { // Display the question text display(); // Display the answer choices . . . } vs void ChoiceQuestion::display() const { // Display the question text Question::display(); // Display the answer choices . . . }

this won't quite work. Because the implicit parameter of ChoiceQuestion::display is of type ChoiceQuestion, and there is a function named display in the ChoiceQuestion class, that function will be called—but that is just the function you are currently writing! The function would call itself over and over. To display the question text, you must be more specific about which function named display you want to call. You want Question::display:

To illustrate this point, let us implement the add_choice member function. The function has how many parameters: The purpose of the function/s is to? Why do we use the to string void ChoiceQuestion::add_choice(string choice, bool correct) { choices.push_back(choice); if (correct) { // Convert choices.size() to string string num_str = to_string(choices.size()); // Set num_str as the answer . . . } }

two, the choice to be added (which is appended to the vector of choices), and a Boolean value to indicate whether this choice is correct. If it is true, set the answer to the current choice number. (We use the to_string function to convert the number to a string—see Section 8.4 for details.)

The _____ reserved word must be used in the base class. All functions with the same _________________ in _______ classes are then automatically virtual. However, it is considered good taste to supply ________________________

virtual name and parameter variable types derived the virtual reserved word for the derived-class functions as well.

Whenever a _______ is called, the _________ determines the type of the implicit parameter in the particular call at ________.

virtual function compiler implicit run time

Consider the display function of the ChoiceQuestion class. It needs to override the base-class display function in order to show the choices for the answer. Specifically, the derived-class function needs to •Display the question text. •Display the answer choices. But how do you get the question text? You can't access the text member of the base class directly because it is private.

void ChoiceQuestion::display() const { // Display the question text Question::display(); // OK // Display the answer choices . . . }

The slicing problem commonly occurs when a function has a polymorphic parameter (that is, a parameter that can belong to a base class or a derived class). In that case, the parameter variable must be a pointer or a reference. Consider this example: void ask(Question q) { q.display(); cout << "Your answer: "; getline(cin, response); cout << q.check_answer(response) << endl; } Where is the issue?

void ask(Question q) // Error { q.display(); cout << "Your answer: "; getline(cin, response); cout << q.check_answer(response) << endl; } If you call this function with a ChoiceQuestion object, then the parameter variable q is initialized with a copy of that object. But q is a Question object; the derived-class information is sliced away. The simplest remedy is to use a reference: void ask(const Question& q) Now only the address is passed to the function. A reference is really a pointer in disguise. No slicing occurs, and virtual functions work correctly.

Consider the display function of the ChoiceQuestion class. It needs to override the base-class display function in order to show the choices for the answer. Specifically, the derived-class function needs to

•Display the question text. •Display the answer choices.

A ChoiceQuestion object differs from a Question object in three ways: 1. 2. 3.

•Its objects store the various choices for the answer. •There is a member function for adding another choice. •The display function of the ChoiceQuestion class shows these choices so that the respondent can choose one of them. When the ChoiceQuestion class inherits from the Question class, it needs only to spell out these three differences: class ChoiceQuestion : public Question { public: ChoiceQuestion(); void add_choice(string choice, bool correct); void display() const; private: vector<string> choices; };

After a programmer has set the questiontext and the several multiple choice answers the ChoiceQuestion object is asked to display something like: In which country was the inventor of C++ born? 1: Australia 2: Denmark 3: Korea 4: United States Question must have

•Storage for the various choices for the answer -Question has the question 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 rewritethe display function display. •This is called overriding a member function.


Ensembles d'études connexes

CH 10 - Making Capital Decisions (Fin Mngmt)

View Set

ASQ: Ch 13 Problem-Solving Tools (P 314 - 352)

View Set

COSC 3332 - Computer Organization and Architecture

View Set

Peripheral Nervous System / Reflexes (Ch 13)

View Set

Exam 2 Multiple Choice Semester 1

View Set