390 likes | 588 Views
C++ Certificate Program C++ Intermediate. Inheritance in Depth. Topics. Multiple Inheritance Access Control Pointers to Members. Multiple Inheritance. class C : public A, public B { public : C(): A(), B() {} };. MI Overview.
E N D
C++ Certificate ProgramC++ Intermediate Inheritance in Depth
Topics • Multiple Inheritance • Access Control • Pointers to Members
Multiple Inheritance class C : public A, public B { public : C(): A(), B() {} };
MI Overview • Mechanism that allows more than one direct base class (versus single inheritance) • Provides union of interfaces presented by the direct base classes
MI Behavior • Virtual methods operate in the standard way • overloaded in the standard way • overridden in the standard way • Access control behaves in the same way • cannot use inheritance to circumvent access control • Polymorphism works in the standard way • C&/C* can be used where B&/B* or A&/A* occurs without loss of information
MI Typical Uses • Most often a means of code reuse by pooling in multiple implementations • Review: protected methods and composition provide for code inheritance rather than interface inheritance • Used when required to exposed the interfaces of both direct base classes • Possible to override methods in the base classes
Ambiguity Potential class A { public : A() {} virtual void foo(); virtual void bar(); }; class B { public : A() {} virtual void foo(); virtual void blah(); }; class C : public A, public B { public: C() : A(), B() {} };
Ambiguity void func( A* a, B* b, C* c) { a->foo(); // ok, uses A::foo b->foo(); // ok, uses B::foo c->foo(): // ambiguous! which foo? }
Resolving Ambiguities • Override explicitly (same name) in derived class class C : public A, public B { public: C() : A(), B() {} void foo() { A::foo();} };
Resolving Ambiguities • Employ using-declaration class C : public A, public B { public: // … using A::foo; };
Overloading Derived Methods with Using-Declaration • Need: compose a set of overloaded methods from base class(es) • Answer: Using-declaration .. this form of using-declaration must refer to methods in the base class(es) • Can not be used to make inaccessible information accessible
Example class A { public : virtual void foo(int); }; class B { public : virtual void foo(std::string const &); virtual void blah(); }; class C : public A, public B { public: using A::foo; // un-hide A::foo using B::foo(); // un-hide A::foo void foo(double); // normally hides A::foo and B::foo };
Replicated Base Classes • Multiple inheritance allows a class to be base more than once • By default, each base object is unique • Virtual base classes can combine common base objects (more detail ahead)
Example class A { public: void foo(); void bar(); }; class B : public A { }; class C : public A { }; class D : public B, public C { public: D() : B(), C() {} void f(); };
Explicit Referencing in Replicated Base Classes • Inside D, cannot refer to foo() and bar() methods from class A without risking ambiguity (which A base object are we using?) • Need to explicitly reference desired instance • Could also employ using-declaration
Example void D::f() { foo(); // illegal – which foo? B::foo(); // ok, use B’s A::foo C::foo(); // ok, use C’s A::foo }
Virtual Base Classes • Sometimes replicated instances are not appropriate • E.g. need a common state in the base • Usually due to implementation details (e.g. base constructor locks a shared resource) • Virtual base classes allow all derived classes to share single instance of a base (diamond inheritance graph)
Virtual Base Example class A { }; class B : public virtual A { }; class C : public virtual A { }; class D : public B, public C { D() : B(), C() {} void f(); };
Extending Base Behavior • Often want to override behavior in base classes • Replace (override) the functionality in a virtual method • Other times want to extend that behavior • Override base class virtual method, call the base class method, provide additional logic to extend functionality
Example class B : public A { public: void open() ; // virtual method in A }; int B::open() { A::open(); // call base class version // add additional logic here }
Why Use Multiple Inheritance? • Glue two otherwise unrelated implementation classes? • This is code inheritance • Not design-driven • Provide implementations for abstract base classes • ‘Mix-in’ classes • Compose new classes through multiple inheritance • Does not augment type so much as implementation • Deriving privately from std::list might be considered a "mix-in" by some
Overriding Virtual Base Methods • Two different classes may override different virtual functions from the virtual base class • If two different classes override same virtual method from same virtual base class, ambiguity occurs - must resolve in same manner mentioned prior • One function must override all others
Example class A { public: virtual void write() { } }; class B : public virtual A { public: void write(); // override A’s version }; class C : public virtual A { public: void write(); // override A’s version };
Example // problem: which version of write() // gets called? class D : public B, public C { }; // this is OK, write() is overriding // versions in A, B, and C class D : public B, public C { public: void write() { } // overrides A, B, C // versions };
Protected Members and Multiple Inheritance • Classes do not get access to sibling’s protected members • An object can access the protected members of another object of the same type • A class deriving from two siblings has access to protected members in both classes, even though the siblings do not
Multiple Inheritance and Access Control • Any path or multiple paths may be used to access a base class or name • In the following rather contrived example, although A::foo() is not available from D via C, it is available from D via B. • Can use using-declaration to make a protected method part of public interface
Example class A { protected: int foo(); }; class B : public virtual A { }; class C : private virtual A { }; // A::foo is visible via B, but // not via C
Example class D : public B, public C { public: D() : B(), A() {} // A::foo() now becomes part of our // public interface using A::foo; };
Using Declaration with Private Inheritance • Private or protected inheritance in conjunction with using-declaration can be used to ‘export’ a subset of an interface • In the following, B exports A::g() and B::x(), but none of A::h(), A::i() or B::y()
Example class A { public : int g(); int h(); int i(); }; class B { public : int x(); int y(); };
Example “Exporting” Methods class C : protected A, protected B { public : using A::g; using B::x; int func(); };
Function Pointers • Taking a pointer to function • rt (*tag)(signature); • where rt is the return type, and signature is the argument list signature
Taking Address of Function int foo(double d) { } int main() { // & optional in next line int (*pfoo)(double) = &foo; pfoo(2.0); // use like normal function }
Pointers to Members • Pointers to members are similar, but slightly different • Require scope accessors • Cannot directly take address of member - are typically offsets into object (may need to consult v-table) • Can be used polymorphically • Static methods are same as regular functions
Example Class A { public: int foo(double); int bar(double); }; // typedefs for this are common typedef int (A::*Fptrdef)(double); int f (A& a) { int (A::*fptr)(double) = a.foo; a->fptr(2); // must be called in context of an object, // hence the ‘a->’ fptr = a.bar; a->fptr(100.5); Fptrdef f = a.bar; a->f(5.1); }
Late Binding • Virtual methods use ‘late-binding’ (runtime selection of appropriate derived method) • When a class has one or more virtual methods, a redirection mechanism is implemented • Typically, mechanism consists of virtual pointer, or v-ptr, and virtual table, or v-table • Object stores a v-ptr, typically at offset 0, that points to a v-table • V-table contains addresses (function pointers) for virtual methods of the class
Example class A { public: virtual void foo() =0; virtual void bar(); }; class B : public A { public: void foo(); // use A::bar(); }; class C { public: void blah(); };
V-Tables • V-table for A • foo() -> NULL • bar() -> A::bar() • V-table for B • foo() -> B::foo() • bar() ->A::bar() • Class C has no virtual methods, and hence has no v-table
Selecting Virtual Methods • When a virtual method is invoked, compiler adds code to reference address of that method from v-table • Object is created from most-derived class, v-ptr constructed to point to proper v-table … if base pointer taken, v-ptr will cause proper method to be invoked • Works only with addresses, i.e., pointers and references, not objects • Slight overhead in virtual methods, “use ’em only when you need ‘em” • In case of A::foo() above, if class has one or more pure virtuals, cannot instantiate an instance of type (foo() has no address and will go boom)