# CSE332S Object-Oriented Programming in C++ (Lecture 12) ## Object-Oriented Programming (OOP) in C++ Today: 1. Type vs. Class 2. Subtypes and Substitution 3. Polymorphism a. Parametric polymorphism (generic programming) b. Subtyping polymorphism (OOP) 4. Inheritance and Polymorphism in C++ a. construction/destruction order b. Static vs. dynamic type c. Dynamic binding via virtual functions d. Declaring interfaces via pure virtual functions ## Type vs. Class, substitution ### Type (interface) vs. Class Each function/operator declared by an object has a signature: name, parameter list, and return value The set of all public signatures defined by an object makes up the interface to the object, or its type - An object’s type is known (what can we request of an object?) - Its implementation is not - different objects may implement an interface very differently - An object may have many types (think interfaces in Java) An object’s class defines its implementation: - Specifies its state (internal data and its representation) - Implements the functions/operators it declares ### Subtyping: Liskov Substitution Principle An interface may contain other interfaces! A type is a **subtype** if it contains the full interface of another type (its **supertype**) as a subset of its own interface. (subtype has more methods than supertype) **Substitutability**: if S is a subtype of T, then objects of type T may be replaced with objects of type S Substitutability leads to **polymorphism**: a single interface may have many different implementations ## Polymorphism Parametric (interface) polymorphism (substitution applied to generic programming) - Design algorithms or classes using **parameterized types** rather than specific concrete data types. - Any class that defines the full interface required of the parameterized type (is a **subtype** of the parameterized type) can be substituted in place of the type parameter **at compile-time**. - Allows substitution of **unrelated types**. ### Polymorphism in OOP Subtyping (inheritance) polymorphism: (substitution applied to OOP) - A derived class can inherit an interface from its parent (base) class - Creates a subtype/supertype relationship. (subclass/superclass) - All subclasses of a superclass inherit the superclass’s interface and its implementation of that interface. - Function overriding - subclasses may override the superclass’s implementation of an interface - Allows the implementation of an interface to be substituted at run-time via dynamic binding ## Inheritance in C++ - syntax ### Forms of Inheritance in C++ A derived class can inherit from a base class in one of 3 ways: - Public Inheritance ("is a", creates a subtype) - Public part of base class remains public - Protected part of base class remains protected - Protected Inheritance ("contains a", **derived class is not a subtype**) - Public part of base class becomes protected - Protected part of base class remains protected - Private Inheritance ("contains a", **derived class is not a subtype**) - Public part of base class becomes private - Protected part of base class becomes private So public inheritance is the only way to create a **subtype**. ```cpp class A { public: int i; protected: int j; private: int k; }; class B : public A { // ... }; class C : protected A { // ... }; class D : private A { // ... }; ``` Class B uses public inheritance from A - `i` remains public to all users of class B - `j` remains protected. It can be used by methods in class B or its derived classes Class C uses protected inheritance from A - `i` becomes protected in C, so the only users of class C that can access `i` are the methods of class C - `j` remains protected. It can be used by methods in class C or its derived classes Class D uses private inheritance from A - `i` and `j` become private in D, so only methods of class D can access them. ## Construction and Destruction Order of derived class objects ### Class and Member Construction Order ```cpp class A { public: A(int i) : m_i(i) { cout << "A" << endl;} ~A() {cout<<"~A"<x (); // prints "B::x": lookup the type of bp, which is B, and x() is non-virtual so it is statically bound bp->y (); // prints "B::y": lookup the dynamic type of bp, which is B (at run-time), and call the overridden y() function ap->x (); // prints "A::x": lookup the type of ap, which is A, and x() is non-virtual so it is statically bound ap->y (); // prints "B::y": lookup the dynamic type of ap, which is B (at run-time), and call the overridden y() function of class B return 0; }; ``` Only matter with pointer or reference - Calls on object itself resolved statically - E.g., `b.y();` Look first at pointer/reference type - If non-virtual there, resolve statically - E.g., `ap->x();` - If virtual there, resolve dynamically - E.g., `ap->y();` Note that virtual keyword need not be repeated in derived classes - But it’s good style to do so Caller can force static resolution of a virtual function via scope operator - E.g., `ap->A::y();` prints “A::y” Potential Problem: Class Slicing When a derived type may be caught by a catch block, passed into a function, or returned out of a function that expects a base type: - Be sure to catch by reference - Pass by reference - Return by reference Otherwise, a copy is made: - Loses original object's "dynamic type" - Only the base parts of the object are copied, resulting in the class slicing problem ## Class (implementation) Inheritance VS. Interface Inheritance Class is the implementation of a type. - Class inheritance involves inheriting interface and implementation - Internal state and representation of an object Interface is the set of operations that can be called on an object. - Interface inheritance involves inheriting only a common interface - What operations can be called on an object of the type? - Subclasses are related by a common interface - But may have very different implementations In C++, pure virtual functions make interface inheritance possible. ```cpp class A { // the abstract base class public: virtual void x() = 0; // pure virtual function, no default implementation virtual void y() = 0; // pure virtual function, no default implementation }; class B : public A { // B is still an abstract class because it still has a pure virtual function y() that is not defined public: virtual void x(); }; class C : public B { // C is a concrete derived class because it has all the pure virtual functions defined public: virtual void y(); }; int main () { A * ap = new C; // ap is a pointer to an abstract class type, but it can point to a concrete derived class object, cannot create an object of an abstract class, for example, new A() will be an error. ap->x (); ap->y (); delete ap; return 0; }; ``` Pure Virtual Functions and Abstract Base Classes: A is an **abstract (base) class** - Similar to an interface in Java - Declares pure virtual functions (=0) - May also have non-virtual methods, as well as virtual methods that are not pure virtual Derived classes override pure virtual methods - B overrides `x()`, C overrides `y()` Can't instantiate an abstract class - class that declares pure virtual functions - or inherits ones that are not overridden A and B are abstract, can create a C Can still have a pointer or reference to an abstract class type - Useful for polymorphism ## Review of Inheritance and Subtyping Polymorphism in C++ Create related subclasses via public inheritance from a common superclass - All subclasses inherit the interface and its implementation from the superclass Override superclass implementation via function overriding - Relies on virtual functions to support dynamic binding of function/operator calls Use pure virtual functions to declare a common interface that related subclasses can implement - Client code uses the common interface, does not care how the interface is defined. Reduces complexity and dependencies between objects in a system.