15: Inheritance and Polymorphism

Ace your homework & exams now with Quizwiz!

Can you compile and run the following code? What will be the output? #include <iostream> using namespace std; class A { public: virtual void f() = 0; }; class B: public A { public: void f() { cout << "invoke f from B" << endl; } }; class C: public B { public: virtual void m() = 0; }; class D: public C { public: virtual void m() { cout << "invoke m from D" << endl; } }; void p(A& a) { a.f(); } int main() { D d; p(d); d.m(); return 0; }

invoke f from B invoke m from D

What is the output of the following code? #include <iostream> using namespace std; class A { public: A() { i = 1; } int getI() { return i; } private: int i; }; class B: public A { public: B() { i = 2; } int getI() { return i; } private: int i; }; int main() { B b; cout << b.getI() << endl; return 0; }

2

What is the output of the following code? #include <iostream> using namespace std; class A { public: A() { i = 1; } virtual int getI() { return i; } private: int i; }; class B: public A { public: B() { i = 2; } int getI() { return i; } private: int i; }; int main() { A* p = new B(); cout << p->getI() << endl; return 0; }

2

What is wrong in the following code? class A { public: virtual void f() = 0; }; int main() { A a; return 0; }

A is an abstract class. You cannot create an instance from an abstract class.

Supertype

A type by its base class

What is the output of the following code? #include <iostream> using namespace std; class B { public: ~B() { cout << "B"; } }; class A: public B { public: ~A() { cout << "A"; } }; int main() { A a; return 0; }

AB

What will be displayed by the following code? #include <iostream> #include <string> using namespace std; class A { public: string toString() { return "A"; } }; class B: public A { public: string toString() { return "B"; } }; int main() { B b; cout << static_cast<A>(b).toString() << b.toString() << endl; return 0; }

AB

Generic programming

Allows functions to be used generically for a wide range of object arguments through polymorphism

If a base class has a customized destructor, are you required to implement the destructor in the derived class?

No. When a destructor for a derived class is invoked, it automatically invokes the destructor in the base class. So, there is no need to explicitly invoke the destructor in the base class. The destructor in the derived class only needs to destroy the dynamically created memory in the derived class.

True or false? A derived class is a subset of a base class.True or false? A derived class is a subset of a base class.

False. A derived class is an extension of a base class and normally contains more details information than its base class.

If a member is declared private in a class, can it be accessed from other classes? If a member is declared protected in a class, can it be accessed from other classes? If a member is declared public in a class, can it be accessed from other classes?

If a member is declared private in a class, can it be accessed from other classes? No. If a member is declared protected in a class, can it be accessed from other classes? Only accessible from derived classes. If a member is declared public in a class, can it be accessed from other classes? Yes.

Dynamic binding

Implements the call for whether a function is defined in a superclass or overridden in a subclass during runtime.

How do you define a pure virtual function?

Just like a virtual function except that you have to add = 0; at the end of the function prototype.

Assume class A is derived from class B and B is derived from C, all the classes have no-arg constructors. Given function f(B b), can you invoke f(A()), f(B()), and f(C())?

You can invoke f(A()) because A() is an instance B since A is derived from B. You can invoke f(B()) because B() is an instance B. You cannot invoke f(C()) because C() is not an instance B.

Can a class be derived from multiple base classes in C++?

Yes.

What is the output of the following code? #include <iostream> using namespace std; class A { public: ~A() { cout << "A"; } }; class B: public A { public: ~B() { cout << "B"; } }; int main() { A* p = new B(); delete p; return 0; }

A

What is upcasting? What is downcasting?

Upcasting is to cast a pointer from a derived class type to a base class type. Downcasting is to cast a pointer from a base class type to a derived class type.

Constructor chaining

When constructing an instance of a class invokes all the constructors, chaining base classes along the inheritance chain

Destructor chaining

When destructing an instance of a class invokes all the destructors along the inheritance chain with the derived class's destructor invoked first

Derived class

A class the inherits from or extends a base class

What is a subtype and a supertype? What is polymorphism?

A class defines a type. A type defined by a subclass is called a subtype, and a type defined by its superclass is called a supertype. Polymorphism means that a variable of a supertype can refer to a subtype object.

Child class

A derived class from a base class

Abstract class

A superclass declared by using the abstract modifier that does not have any specific common features shared by subclasses. Unlike regular classes, instances cannot be created.

What will be the output if int is changed to long in line 7 in LiveExample 15.12? #include <iostream> using namespace std; class A { public: virtual void print(long i) { cout << "A" << i << endl; } }; class B : public A { public: // Suppose to override the print function in A void print(long i) { cout << "B" << i << endl; } }; int main() { A* p1 = new B(); B* p2 = new B(); p1->print(1); p2->print(2); return 0; }

B1 B2

What is the output of the following code? #include <iostream> using namespace std; class A { public: ~A() { cout << "A"; } }; class B: public A { public: ~B() { cout << "B"; } }; int main() { B* p = new B(); delete p; return 0; }

BA

Upcasting

Casting an object from a subclass type to superclass

When do you need to downcast an object from a base class type to a derived class type?

If a pointer points to an object of a derived class, but it is declared as a base class type, you need to cast the pointer to the derived class type in order to access the members defined in the derived class.

Are the constructors inherited by the derived class?

No

Is declaring virtual functions enough to enable dynamic binding?

No. You also need to use reference for the object that invokes the function.

Overriding a function

Redefines a virtual function

Subclass

Subclass refers to derived class

Show the output of the following code: (a) #include <iostream> #include <string> using namespace std; class Person { public: void printInfo() { cout << getInfo() << endl; } virtual string getInfo() const { return "Person"; } }; class Student: public Person { public: string getInfo() const { return "Student"; } }; int main() { Person().printInfo(); Student().printInfo(); } (b) #include <iostream> #include <string> using namespace std; class Person { public: void printInfo() { cout << getInfo() << endl; } string getInfo() const { return "Person"; } }; class Student: public Person { public: string getInfo() const { return "Student"; } }; int main() { Person().printInfo(); Student().printInfo(); }

(a) Person Student (b) Person Person

What is the output of the following code? #include <iostream> using namespace std; class A { public: A() { i = 1; } int getI() { return i; } private: int i; }; class B: public A { public: B() { i = 2; } int getI() { return i; } private: int i; }; int main() { A* p = new B(); cout << p->getI() << endl; return 0; }

1

Base class

A class inherited by a subclass

Which of the following statements is true? - An abstract class is declared using a keyword abstract. - A class is abstract if it contains a pure virtual function. - An abstract class is like a regular class and you can create objects from it. - You can declare a class abstract even though it does not contain abstract functions.

A class is abstract if it contains a pure virtual function.

Which of the following statements is false? - To redefine a function, the function must be defined in the derived class using the same signature and return type as in its base class. - Overloading a function is to provide more than one function with the same same but with different signatures to distinguish them. - It is a compile error if tow functions differ only in return type. - A private function cannot be redefined. If a function defined in a derived class is private in its base class, the two functions are completely unrelated. - A constructor can be redefined.

A constructor can be redefined.

Which of the following statements are true? - A derived class is a subset of a base class. - A derived class is usually extended to contain more functions and more detailed information than its base class. - "class A: B" means A is a derived class of B. - "class A: public B" means B is a derived class of A.

A derived class is usually extended to contain more functions and more detailed information than its base class.

Pure virtual function

A function declaration without implementation. Also known as abstract function.

Virtual

A function declared with the keyword virtual. In C++, redefining a virtual function in a derived class is called overriding a function.

Abstract functions

A function signature without implementation. Its implementation is provided by its subclasses. An abstract function is denoted with an abstract modifier and must be contained in an abstract class. In a nonabstract subclass extended from an abstract class, all abstract functions must be implemented, even if they are not used in the subclass.

What is the output of running the program in (a)? What problem arises in compiling the program in (b)? (a) #include <iostream> using namespace std; class Parent { public: Parent() { cout << "Parent's no-arg constructor is invoked"; } }; class Child: public Parent { }; int main() { Child c; return 0; } (b) #include <iostream> using namespace std; class Parent { public: Parent(int x) { } }; class Child: public Parent { }; int main() { Child c; return 0; }

(a) The output is Parent's no-arg constructor is invoked (b) The default constructor of Child attempts to invoke the default of constructor of Parent, but class Parent's default constructor is not defined.

Analyze the following code: #include <iostream> using namespace std; class Parent { }; class Child: public Parent { public: void m() { cout << "invoke m" << endl; } }; int main() { Parent* p = new Child(); // To be replaced return 0; } (a) What compile errors will you get if the "To be replaced" line is replaced by the following code? (*p).m(); (b) What compile errors will you get if the "To be replaced" line is replaced by the following code? Child* p1 = dynamic_cast<Child*>(p); (*p1).m(); (c) Will the program compile and run if the highlighted line is replaced by the following code? Child* p1 = static_cast<Child*>(p); (*p1).m(); (d) Will the program compile and run if virtual void m() { } is added in the Parent class and the highlighted line is replaced by dynamic_cast<Child*>(p)->m();?

(a) m() is not a member of the Parent class. (b) To cast p dynamically, p's defining class must have virtual functions. Here, Parent is not defined as a class with virtual functions. (c) Static casting to the Child type is OK. (d) Yes.

What will be the value in p1 after the following statements? GeometricObject* p = new Rectangle(2, 3); Circle* p1 = new Circle(2); p1 = dynamic_cast<Circle*>(p);

0

What is the output of the following code? #include <iostream> using namespace std; class ParentClass { public: int id; ParentClass(int id) { this->id = id; } void print() { cout << id << endl; } }; class ChildClass: public ParentClass { public: int id; ChildClass(int id): ParentClass(1) { this->id = id; } }; int main() { ChildClass c(2); c.print(); return 0; }

1

What relationship is appropriate for the following classes? Draw the relationships using UML diagrams. 1 Company and Employee 2 Course and Faculty 3 Student and Person 4 House and Window 5 Account and Savings Account

1. Composition 2. Composition 3. Inheritance 4. Composition 5. Inheritance

Protected

A modifier for members of a class. A protected member of a class can be used in the class in which it is declared or any subclass derived form that class.

Subtype

A type defined by a derived class

What would happen if int is changed to long in line 17 in LiveExample 15.13? #include <iostream> using namespace std; class A { public: virtual void print(int i) { cout << "A" << i << endl; } }; class B : public A { public: // Use override keyword to avoid errors void print(long i) override { cout << "B" << i << endl; } }; int main() { A* p1 = new B(); B* p2 = new B(); p1->print(1); p2->print(2); return 0; }

A1 B2

What will be displayed by the following code? #include <iostream> #include <string> using namespace std; class C { public: virtual string toString() { return "C"; } }; class B: public C { public: string toString() { return "B"; } }; class A: public B { public: string toString() { return "A"; } }; void displayObject(C* p) { cout << p->toString(); } int main() { displayObject(&A()); displayObject(&B()); displayObject(&C()); return 0; }

ABC

True or false? (1) You can redefine a private function defined in a base class. (2) You can redefine a static function defined in a base class. (3) You can redefine a constructor.

All false. (1) No. You can only override accessible functions. (2) Yes. (3) No.

Show the output of following program: #include <iostream> #include <string> using namespace std; class Apple { public: Apple(); Apple(double weight); virtual string toString(); protected: double weight; }; Apple::Apple() { weight = 1; cout << "Apple no-arg constructor" << endl; } Apple::Apple(double weight) { this->weight = weight; cout << "Apple constructor with weight" << endl; } string Apple::toString() { return "Apple: " + to_string(weight); } class GoldenDelicious : public Apple { public: GoldenDelicious(); GoldenDelicious(double weight); string toString() override; }; GoldenDelicious::GoldenDelicious() : Apple(5) { cout << "GoldenDelicious non-arg constructor" << endl; } GoldenDelicious::GoldenDelicious(double weight) : Apple(weight) { cout << "GoldenDelicious constructor with weight" << endl; } string GoldenDelicious::toString() { return "GoldenDelicious: " + to_string(weight); } int main() { Apple* a = new Apple(); cout << a->toString() << endl; cout << "---------------" << endl; GoldenDelicious* g = new GoldenDelicious(7); cout << g->toString() << endl; cout << "---------------" << endl; Apple* c = new GoldenDelicious(8); cout << c->toString() << endl; return 0; }

Apple constructor with weight Apple no-arg constructor Apple: 1.0 --------------- Apple constructor with weight GoldenDelicious constructor with weight GoldenDelicious: 7.0 --------------- Apple constructor with weight GoldenDelicious constructor with weight GoldenDelicious: 8.0

What will be displayed by the following code? #include <iostream> #include <string> using namespace std; class C { public: string toString() { return "C"; } }; class B: public C { public: string toString() { return "B"; } }; class A: public B { public: string toString() { return "A"; } }; void displayObject(C* p) { cout << p->toString(); } int main() { displayObject(&A()); displayObject(&B()); displayObject(&C()); return 0; }

CCC

What will be displayed by the following code? #include <iostream> #include <string> using namespace std; class C { public: string toString() { return "C"; } }; class B: public C { public: string toString() { return "B"; } }; class A: public B { public: virtual string toString() { return "A"; } }; void displayObject(C* p) { cout << p->toString(); } int main() { displayObject(&A()); displayObject(&B()); displayObject(&C()); return 0; }

CCC

What will be displayed by the following code? #include <iostream> #include <string> using namespace std; class C { public: virtual string toString() { return "C"; } }; class B: public C { public: string toString() { return "B"; } }; class A: public B { public: string toString() { return "A"; } }; void displayObject(C p) { cout << p.toString(); } int main() { displayObject(A()); displayObject(B()); displayObject(C()); return 0; }

CCC

Downcasting

Casting an object from a superclass type to subclass

Suppose you declared GeometricObject* p = &object. To cast p to Circle, use ___________.

Circle* p1 = dynamic_cast<Circle*>(p);

Inheritance

Declares a new class by extending an existing class

True or false? When invoking a constructor from a derived class, its base class's no-arg constructor is always invoked.

False . If a derived class's constructor explicitly invoke a base class's constructor, the base class's no-arg constructor is not invoked.

Object-oriented programming allows you to derive new classes from existing classes. This is called ____________.

Inheritance

Why should you define a destructor virtual?

It is a good practice to always declare destructors virtual to ensure that a derived class' constructor is called when an object of the derived class is deleted.

What is static binding and what is dynamic binding?

Matching a function signature and binding a function implementation are two separate issues. The declared type of the variable decides which function to match at compile time. This is static binding. The compiler finds a matching function according to parameter type, number of parameters, and order of the parameters at compile time. A virtual function may be implemented in several derived classes. C++ dynamically binds the implementation of the function at runtime, decided by the actual class of the object referenced by the variable. This is dynamic binding.

Can destructors be overloaded? Can you redefine a destructor?

No. You cannot overload a destructor. No. You cannot redefine a destructor in a derived class.

Is it a good practice to define all functions virtual?

No. it is more efficient without declaring it virtual, because it takes more time and system resource to bind virtual functions dynamically at runtime. You should only declare a function virtual if it is intended to be redefined by derived classes.

What is the difference between overloading a function and redefining a function?

Overloading a function is a way to provide more than one function with the same name but with different signatures to distinguish them. To redefine a function, the function must be defined in the derived class using the same signature and same return type as in its base class.

Show the output of the following code: #include <iostream> using namespace std; class Parent { public: Parent() { cout << "Parent's no-arg constructor is invoked" << endl; } ~Parent() { cout << "Parent's destructor is invoked" << endl; } }; class Child: public Parent { public: Child() { cout << "Child's no-arg constructor is invoked" << endl; } ~Child() { cout << "Child's destructor is invoked" << endl; } }; int main() { Child c1; Child c2; return 0; }

Parent's no-arg constructor is invoked Child's no-arg constructor is invoked Parent's no-arg constructor is invoked Child's no-arg constructor is invoked Child's destructor is invoked Parent's destructor is invoked Child's destructor is invoked Parent's destructor is invoked

Polymorphism

Refers to the feature that an object of a subclass can be used by any code designed to work with an object of its superclass

Parent class

Serves as a base for defining a derived class and is the same as a base class

Superclass

Superclass refers to base class

The getArea and getPerimeter functions may be removed from the GeometricObject class. What are the benefits of defining getArea and getPerimeter as abstract functions in the GeometricObject class?

The benefits are for generic programming. A variable of GeometricObject type can use the getArea and getPerimeter functions at compilation time.

Analyze the following code: #include <iostream> using namespace std; class A { public: A() { t(); //cout << "i from A is " << i << endl; } void t() { setI(20); } virtual void setI(int i) { this->i = 2 * i; } int i; }; class B: public A { public: B() { cout << "i from B is " << i << endl; } void setI(int i) override { this->i = 3 * i; } }; int main() { A* p = new B(); return 0; }

The constructor of class A is called and it displays "i from B is 40".

Which of the following statements is false? - Assigning a pointer of a derived class type to a pointer of its base class type is called upcasting. - Assigning a pointer of a base class type to a pointer of its derived class type is called downcasting. - Upcasting can be performed implicitly without using the dynamic_cast operator. - Downcasting must be performed explicitly using the dynamic_cast operator. - The dynamic_cast operator can cast a primitive type variable to another primitive type.

The dynamic_cast operator can cast a primitive type variable to another primitive type.

What is the purpose of using the final keyword?

The final keyword can be used in two ways: (1) To prevent a function to be overridden; (2) To declare a constant.

Identify the problems in the following classes. class Circle { public: Circle(double radius) { radius = radius; } double getRadius() { return radius; } double getArea() { return radius * radius * 3.14159; } private: double radius; }; class B : Circle { public: B(double radius, double length) { radius = radius; length = length; } // Returns Circle's getArea * length double getArea() { return getArea() * length; } private: double length; };

The following lines are erroneous: { radius = radius; // Must use this->radius = radius } class B: public Circle (missing public) { length = length; // Must use this->length = length } { Circle(radius); // Must use super(radius) length = length; // Must use this.length = length } double getArea() { return getArea() * length; // Must be Circle::getArea() * length }

Which of the following statements is incorrect? class Fruit { public: Fruit(int id) { } }; class Apple: public Fruit { public: Apple() { } }; - The program will compile if you add a no-arg constructor for Fruit. - The program has a compile error because Fruit does not have a no-arg constructor. - The program will compile if you delete the constructor in Fruit. - The program will compile if you replace Apple() by Apple(): Fruit(4). - The program will compile fine.

The program will compile fine.

Which of the following statements if false? - Private members can only be accessed from the inside of the class unless from a friend class or a friend function. - A protected data field or a protected function in a base class can be accessed by name in its derived classes. - A public data field or function in a class can be accessed by name in any other program. - You cannot have a protected constructor.

You cannot have a protected constructor. If a class uses a protected constructor, the constructor can only be used by its derived class. The derived class can use it to initialize data fields in the base class.

If a base class has a customized copy constructor and assignment operator, how should you define the copy constructor and the assignment operator in the derived class?

You should implement the copy constructor and assignment operator by invoking the copy constructor or the assignment operator function in the base class to copy the appropriate data fields in the base class.

Answer the following questions for the program below: 1 #include <iostream> 2 using namespace std; 3 4 class Parent 5 { 6 public: 7 void f() 8 { 9 cout << "invoke f from Parent" << endl; 10 } 11 }; 12 13 class Child: public Parent 14 { 15 public: 16 void f() 17 { 18 cout << "invoke f from Child" << endl; 19 } 20 }; 21 22 void p(Parent a) 23 { 24 a.f(); 25 } 26 27 int main() 28 { 29 Parent a; 30 a.f(); 31 p(a); 32 33 Child b; 34 b.f(); 35 p(b); 36 37 return 0; 38 } a. What is the output of this program? b. What will be the output if line 7 is replaced by virtual void f()? c. What will be the output if line 7 is replaced by virtual void f() and line 22 is replaced by void p(Parent& a)?

a. invoke f from Parent invoke f from Parent invoke f from Child invoke f from Parent b. invoke f from Parent invoke f from Parent invoke f from Child invoke f from Parent c. invoke f from Parent invoke f from Parent invoke f from Child invoke f from Child

To invoke the toString() function defined in GeometricObject from a Circle object c, use __________.

c.GeometricObject::toString()

Assume the existence of a Phone class. Define a derived class, CameraPhone that contains two data members: an int named, imageSize, representing the size in megabytes of each picture, and an int named memorySize, representing the number of megabytes in the camera's memory. There is a constructor that accepts two integer parameters corresponding to the above two data members and which are used to initialize the respective data members. There is also a function named numPictures that returns (as an int) the number of pictures the camera's memory can hold. Note: You need to define the CameraPhone class and also implement the CameraPhone's constructor and the numPictures function.

class CameraPhone: public Phone { public: CameraPhone(int, int); int numPictures(); private: int imageSize; int memorySize; }; CameraPhone::CameraPhone(int a, int b) { imageSize = a; memorySize = b; } int CameraPhone::numPictures() { return memorySize/imageSize; }

Assume the existence of a BankAccount class. Define a derived class, SavingsAccount that contains two instance variables: the first a double, named interestRate, and the second an integer named interestType. The value of the interestType variable can be 1 for simple interest and 2 for compound interest. There is also a constructor that accepts two parameters: a double that is used to initialize the interestRate variable, and a string that you may assume will contain either "Simple", or "Compound", and which should be used to initialize the interestType variable appropriately. There should also be a pair of functions getInterestRate and getInterestType that return the values of the corresponding data members (as double and int respectively). Note: You need to define the SavingsAccount class and also implement the SavingsAccount's constructor and the getInterestRate and getInterestType functions.

class SavingsAccount : public BankAccount { private: double interestRate; int interestType; public: SavingsAccount() { interestRate = 0; interestType = 0; } SavingsAccount(double, string); double getInterestRate() const { return interestRate; } int getInterestType() const { return interestType; } }; SavingsAccount::SavingsAccount(double a, string b) { interestRate = a; if (b == "Simple") interestType = 1; else interestType = 2; }

Assume the existence of a Window class with a function getWidth that returns the width of the window. Define a derived class WindowWithBorder that contains a single additional integer instance variable named borderWidth, and has a constructor that accepts an integer parameter which is used to initialize the instance variable. There is also a function getUseableWidth, that returns the width of the window minus the width of the border. Note: You need to define the WindowWithBorder class and also implement the WindowWithBorder's constructor and the getUseableWidth function.

class WindowWithBorder : public Window { public: WindowWithBorder(int); int getUseableWidth(); private: int borderWidth; }; WindowWithBorder::WindowWithBorder(int a) { borderWidth = getWidth() - a; } int WindowWithBorder::getUseableWidth() { return borderWidth; }

Suppose Circle and Rectangle classes are derived from GeometricObject and the displayGeometricObject function is defined as shown below. Which of the following function calls is incorrect? void displayGeometricObject(GeometricObject shape) { cout << shape.toString() << endl; } - displayGeometricObject(GeometricObject("black", true)); - displayGeometricObject(Rectangle(2, 3)); - displayGeometricObject(string()); - displayGeometricObject(Circle(5));

displayGeometricObject(string());

Show the output of the following code. #include <iostream> using namespace std; class A { public: A() { t(); cout << "i from A is " << i << endl; } void t() { setI(20); } virtual void setI(int i) { this->i = 2 * i; } int i; }; class B: public A { public: B() { cout << "i from B is " << i << endl; } void setI(int i) { this->i = 3 * i; } }; int main() { A* p = new B(); p->t(); cout << p->i << endl; return 0; }

i from A is 40 i from A is 40 60

Which of the following is an abstract function? - virtual double getArea(); - virtual double getArea() = 0; - double getArea() = 0; - double getArea();

virtual double getArea() = 0;


Related study sets

ECO1017 - Keynes, IS-LM, Multiplier, Accelerator and AD

View Set

Life Insurance Policy Provisions, Options and Riders

View Set

Medical-Surgical HESI Study Questions (Saunders HESI 7th Edition)

View Set

Chapter 7 Personal Finance (for exam 3)

View Set