+ All Categories
Home > Documents > C++ Training Datascope Lawrence D’Antonio

C++ Training Datascope Lawrence D’Antonio

Date post: 12-Jan-2016
Category:
Upload: jolie
View: 21 times
Download: 0 times
Share this document with a friend
Description:
C++ Training Datascope Lawrence D’Antonio. Lecture 8 An Overview of C++: What is Polymorphism? – Subtype Polymorphism and Inheritance. Do not multiply objects without necessity W. Occam. What is polymorphism?. Parametric. Universal. Subtype. Polymorphism. Overloading. Ad-hoc. - PowerPoint PPT Presentation
Popular Tags:
323
C++ Training C++ Training Datascope Datascope Lawrence D’Antonio Lawrence D’Antonio Lecture 8 Lecture 8 An Overview of C++: An Overview of C++: What is Polymorphism? – What is Polymorphism? – Subtype Polymorphism and Subtype Polymorphism and Inheritance Inheritance
Transcript
Page 1: C++ Training Datascope Lawrence D’Antonio

C++ TrainingC++ TrainingDatascopeDatascope

Lawrence D’AntonioLawrence D’AntonioLecture 8Lecture 8

An Overview of C++:An Overview of C++:What is Polymorphism? – What is Polymorphism? –

Subtype Polymorphism and InheritanceSubtype Polymorphism and Inheritance

Page 2: C++ Training Datascope Lawrence D’Antonio

Do not multiply objects without necessity

W. Occam

Page 3: C++ Training Datascope Lawrence D’Antonio

What is polymorphism?What is polymorphism?

Different types of objects respond to the Different types of objects respond to the same message and use the appropriate same message and use the appropriate method.method.

Polymorphism

Universal

Ad-hoc

Parametric

Subtype

Overloading

Coercion

Page 4: C++ Training Datascope Lawrence D’Antonio

Subtype polymorphism

Subtype (or inclusion) polymorphism Subtype (or inclusion) polymorphism allows objects of a given type to be allows objects of a given type to be substituted for by objects of a subtype.substituted for by objects of a subtype.

Page 5: C++ Training Datascope Lawrence D’Antonio

What is inheritance?What is inheritance?

One class (derived/child) relies on the definition of another class (base/parent).

Single vs. multiple inheritance A method of sharing code or sharing

interface among classes. Language may define a class tree (with

single root): Java, Smalltalk Language may define a class forest: C++

Page 6: C++ Training Datascope Lawrence D’Antonio

What does a child inherit?

A child class inherits all of the features of the parent except for the following:

Constructors/destructor The assignment operator Friend functions and classes

Page 7: C++ Training Datascope Lawrence D’Antonio

Access and Inheritance

The following table gives for each type of inheritance, for a member of given access in the parent class what access that member has in the child class.

Page 8: C++ Training Datascope Lawrence D’Antonio

Inheritance Type

Public Protected Private

Access Type

Public Public Protected Private

Protected Protected Protected Private

Private NA NA NA

Page 9: C++ Training Datascope Lawrence D’Antonio

Forms of inheritance

Single: Derived class has one parent Multiple: Derived class has more than one

parent. Interface: Defines how a class may be

implemented. Mixin: A fragment of a class that is meant

to be composed with another.

Page 10: C++ Training Datascope Lawrence D’Antonio

Three views of inheritance

Code sharing – one defined structure can be incorporated into another.

Logical relationship – one class has the relationship of generalization or specialization with respect to the other.

Type – a subtype can be created given the definition of a supertype.

Page 11: C++ Training Datascope Lawrence D’Antonio

Reasons for using inheritance

Code reuse – The child inherits features from the parent, hence the code doesn’t need to be rewritten.

Concept reuse – The child overrides features of the parent. No code is shared, but the semantics of the method is maintained.

Page 12: C++ Training Datascope Lawrence D’Antonio

Inheritance in different languages

C++class B: public A {};

C#class B: A {}

CLOS(defclass B (A) () )

Page 13: C++ Training Datascope Lawrence D’Antonio

Inheritance part 2

Javaclass B extends A {}

Object Pascaltype

B = object(A)…end;

Page 14: C++ Training Datascope Lawrence D’Antonio

Inheritance part 3

Pythonclass B(A):

def __init__ (self):…

Rubyclass B < A…end

Page 15: C++ Training Datascope Lawrence D’Antonio

Inheritance part 4

Eiffel

class B inherit

A

end Ada

type B is new A with …

Page 16: C++ Training Datascope Lawrence D’Antonio

Inheritance part 5

Objective C@interface B: A…

@end Dylandefine class <B> (<A>)…

end class <B>

Page 17: C++ Training Datascope Lawrence D’Antonio

Direct vs. Indirect Base

A base class is direct if it is explicitly mentioned as a base class in the declaration of the child class.

An indirect base class is a base class that is not mentioned explicitly in the child class declaration, but is available to the child class through one of its base classes.

Page 18: C++ Training Datascope Lawrence D’Antonio

Example

Direct base: class A is a direct base of class B

class A {};

class B: public A {};

Page 19: C++ Training Datascope Lawrence D’Antonio

Example

Indirect base: class A is an indirect base of class C.

class A {};

class B: public A {};

class C: public B {};

Page 20: C++ Training Datascope Lawrence D’Antonio

Example

In this example, class A is both a direct and indirect base of class C.

class A {};

class B: public A {};

class C: public A, public B {};

Page 21: C++ Training Datascope Lawrence D’Antonio

Constructors/Destructors

The child class either must directly call the parent’s constructor or else the parent must have a default constructor.

Base classes are initialized in the order of declaration.

Destructors are called in reverse order of the constructors.

Page 22: C++ Training Datascope Lawrence D’Antonio

Example

class A {public:

A() {}};

class B: public A {public:

B() {}};

Page 23: C++ Training Datascope Lawrence D’Antonio

This example is valid because class A has a default constructor. The constructor for class B will, in effect, call this constructor.

Page 24: C++ Training Datascope Lawrence D’Antonio

Example

class A {public:

A(int) {}};

class B: public A {public:

B() {}};

Page 25: C++ Training Datascope Lawrence D’Antonio

This is illegal.

Since class A has a constructor A(int), this means that A will not have a default constructor written for it.

Thus A’s constructor is not called by B’s constructor. This is then an error.

Page 26: C++ Training Datascope Lawrence D’Antonio

Example

class A {public:

A(int) {}};

class B: public A {public:

B(): A(5) {}};

Page 27: C++ Training Datascope Lawrence D’Antonio

This is legal.

B’s constructor explicitly calls A’s constructor.

Page 28: C++ Training Datascope Lawrence D’Antonio

Order of Initialization

Virtual base classes in the order of declaration.

Nonvirtual base classes in the order of declaration.

Class members are initialized in order of declaration.

The body of the constructor is then executed.

Page 29: C++ Training Datascope Lawrence D’Antonio

Order of Destruction

The destructor for a class object is called before destructors for members and bases are called.

Destructors for nonstatic members are called before destructors for base classes are called.

Destructors for nonvirtual bases are called before destructors for virtual base classes.

Page 30: C++ Training Datascope Lawrence D’Antonio

Example

class Employee {std::string first_name, last_name,

ssn;

public://…

};

class Manager: public Employee {std::set<Employee*> group;

public:short level;

};

Page 31: C++ Training Datascope Lawrence D’Antonio

Employee:

first name:

last name:

ssn:

Manager:

first name:

last name:

ssn:

group:

level:

Page 32: C++ Training Datascope Lawrence D’Antonio

Is-A Relationship

A Manager is a type of Employee. So any expression accepting a valid pointer or reference to a base object may accept a pointer or reference to a child object.

Page 33: C++ Training Datascope Lawrence D’Antonio

Example

void f(const Employee & re,

const Manager &rm)

{

std::list<Employee *> le;

le.push_front(&re);

le.push_front(&rm);

}

Page 34: C++ Training Datascope Lawrence D’Antonio

Explanation

Manager “is-an” Employee. Manager* can be used as an Employee*

(likewise for references). Employee* cannot be used as a Manager*

without a cast.

Page 35: C++ Training Datascope Lawrence D’Antonio

Is this legal?class Employee{ std::string first_name, last_name, ssn; };

class Manager: public Employee { std::set<Employee*> group;public: short level;};

void foo(Manager &m, Employee &e){ Employee *pe = &m; Manager *pm = &e;

pm->level = 2; pm = static_cast<Manager*>(pe); pm->level = 2;}

main() { Manager m; Employee e; foo(m,e);};

Page 36: C++ Training Datascope Lawrence D’Antonio

Yes and no.

Employee *pe = &m; //OK, Manager is Employee

Manager *pm = &e; //Not legal, Employee is not a Manager pm->level = 2;//Not legal, e doesn’t have a level

pm = static_cast<Manager*>(pe); //Works because pe points to Manager m

pm->level = 2;//Works because pm points to Manager m that has a//level.

Page 37: C++ Training Datascope Lawrence D’Antonio

Overriding Base class

class Employee { std::string first_name, last_name;public: void print() const;};

void Employee::print() const{ std::cout << "Employee\n";}

Page 38: C++ Training Datascope Lawrence D’Antonio

• Derived class

class Manager: public Employee { std::set<Employee*> group; short level;public: void print() const;}; void Manager::print() const{ Employee::print(); std::cout << "Manager\n";}

Page 39: C++ Training Datascope Lawrence D’Antonio

What’s the output?

Employee e; Manager m; Employee *pe1 = &e; Employee *pe2 = &m; Manager *pm = &m; pe1->print(); pe2->print(); pm->print(); pm->Employee::print();

Page 40: C++ Training Datascope Lawrence D’Antonio

Output

Employee //pe1->print()

Employee //pe2->print()

Employee //pm->print()

Manager //pm->print()

Employee //pm->Employee::print()

Page 41: C++ Training Datascope Lawrence D’Antonio

What’s the output?class A {public: A(int x) { std::cout << "A::x = " << x << '\n'; }};

class B: public A {public: B(int x): A(x-1)

{ std::cout << "B::x = " << x << '\n'; }};

class C: public A, public B {public: C(int x): A(x+1), B(x-1) { std::cout << "C::x = " << x << '\n'; }};

C c(5);

Page 42: C++ Training Datascope Lawrence D’Antonio

Output

A::x = 6 //C::A

A::x = 3 //C::B::A

B::x = 4 //C::B

C::x = 5 //C

Page 43: C++ Training Datascope Lawrence D’Antonio

Slicing

In copying from a child to a parent object, only the data of the parent subobject of the child is copied.

Page 44: C++ Training Datascope Lawrence D’Antonio

Slicing Exampleclass A { int x;public: A(int a): x(a) {}};

class B: public A { int y;public: B(int a, int b): A(a), y(b) {}};

main () { B b(3,4); A a = b; a = b;}

Page 45: C++ Training Datascope Lawrence D’Antonio

What happened?

A a = b;

//Calls A(const A&),

//only copies b.x

a = b;

//Calls A::operator=(const A&)

//only copies b.x

Page 46: C++ Training Datascope Lawrence D’Antonio

Class Hierarchy

class Employee {}; //Base classclass Manager: public Employee {};class Director: public Manager {};

class Temporary {}; //Base classclass Secretary: public Employee {};class TempSec: public Temporary, public Secretary {};class Consultant: public Temporary,

public Manager {};

Page 47: C++ Training Datascope Lawrence D’Antonio

Temporary Employee

Secretary Manager

TempSec

Consultant Director

Page 48: C++ Training Datascope Lawrence D’Antonio

Class Hierarchy Structure

A class hierarchy forms a DAG (directed acyclic graph).

The graph need not be a tree. For example, in the graph below there are two paths from D to A.

A

B C

D

Page 49: C++ Training Datascope Lawrence D’Antonio

Type fields

A simple, but usually inadequate method to create polymorphism is to use type fields.

A type field is a data type (usually an enum) that is placed in the base class for functions to use.

Page 50: C++ Training Datascope Lawrence D’Antonio

Type field example

struct Employee { enum E_type {E,M}; E_type type;

Employee(): type(E) {}};

struct Manager: public Employee { Manager() { type = M; }};

Page 51: C++ Training Datascope Lawrence D’Antonio

Example continuedvoid print_employee(const Employee *pe){ switch(pe->type) { case Employee::E: //print out Employee data break; case Employee::M: Manager *pm =

static_cast<const Manager*>(pe);

//print out Manager data break; ]}

Page 52: C++ Training Datascope Lawrence D’Antonio

Example continued

void print_employee_list(const list<Employee*> & emp_list)

{ for(list<Employee*>::const_iterator p =

emp_list.begin(); p != empl_list.end(); ++p) print_employee(*p);}

Page 53: C++ Training Datascope Lawrence D’Antonio

Virtual Functions

Virtual functions avoid the problems associated with type fields. The base class defines functions that the derive classes may redefine.

The base class doesn’t need to anticipate all the different versions of the function that may be needed.

Page 54: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 2

The compiler guarantees the correspondence between objects and the appropriate version of the function.

Virtual functions use what is known as dynamic binding. The resolution of which version of the function is called will occur at run-time.

Page 55: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 3

Binding is the association of an object with its member.

Dynamic binding gives a program flexibility.

Page 56: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 4

The advantage for clients is the ability to request an operation without explicitly selecting one of its variants.

In large systems, where many variants may be possible; dynamic binding protects each component against changes in other components.

Page 57: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 5

Polymorphism is invoked through virtual functions. In a function call the correct version of the function is selected based on the actual type of the calling object.

Virtual functions are implemented in C++ through the mechanism called the virtual function table.

Page 58: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 6

A derived class may or may not redefine a virtual function inherited from the parent.

If a function is virtual in the parent, then it remains virtual in any class that has the parent as a direct or indirect base (namely, you can’t undo the virtual property).

Page 59: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 7

The word virtual need only be used for the parent class function.

A virtual function must be defined for the class in which it is first declared (except for pure virtual functions).

Page 60: C++ Training Datascope Lawrence D’Antonio

Virtual Functions 8

Any member function may be declared virtual except for constructors. Overloaded operators may be declared virtual.

A virtual function must have the same argument types in the base and derived classes.

Furthermore, only very slight changes are allowed in the return type.

Page 61: C++ Training Datascope Lawrence D’Antonio

Exampleclass Employee { std::string first_name, last_name;public: Employee(const std::string &f,

const std::string &l): first_name(f),last_name(l)

{} virtual void print() const;};

void Employee::print() const{

std::cout << last_name << ", " << first_name << '\n';

}

Page 62: C++ Training Datascope Lawrence D’Antonio

Example Part 2class Manager: public Employee { std::set<Employee*> group; short level;public: Manager(const std::string &f, const std::string &l, short le):

Employee(f,l), level(le) {} void print() const;};

void Manager::print() const{ Employee::print(); std::cout << "\tlevel " << level << '\n';}

Page 63: C++ Training Datascope Lawrence D’Antonio

Example Part 3

void print_list(std::set<Employee*>&s)

{

std::for_each(s.begin(),s.end(),

std::mem_fun(&Employee::print));

}

Page 64: C++ Training Datascope Lawrence D’Antonio

Example Part 4main() { Employee e1("Bob", "Jones"); Employee e2("Joe", "Smith"); Manager m("Mary", "Brown", 3); std::set<Employee*> s; s.insert(&e1); s.insert(&e2); s.insert(&m); print_list(s);

return 0;}

Page 65: C++ Training Datascope Lawrence D’Antonio

Output

Brown, Mary

level 3

Smith, Joe

Jones, Bob

Page 66: C++ Training Datascope Lawrence D’Antonio

What is mem_fun()?

It takes a pointer to member function argument and returns a function object.

Basically this is a function to convert a member function into a global function.

Page 67: C++ Training Datascope Lawrence D’Antonio

Code for mem_fun()

template<class R, class T>

mem_fun_t<R,T> mem_fun(R (T::*pm)())

{

return mem_fun_t<R,T>(pm);

}

Page 68: C++ Training Datascope Lawrence D’Antonio

Code for mem_fun_t()

template<class R, class T>class mem_fun_t: public unary_function<T*,R>

{ R (T::*ptr) ();public: explicit mem_fun_t(R (T::*pm) ()):

ptr(pm) {} R operator()(T *p) const

{ return (p->*ptr)(); }};

Page 69: C++ Training Datascope Lawrence D’Antonio

mem_fun_ref()

What if you wanted to take a member function, convert it into a global function that is passed a reference to the calling object?

mem_fun() is passed a pointer, uses the -> operator to call the function.

To use a reference to the calling object, use mem_ref_fun().

Page 70: C++ Training Datascope Lawrence D’Antonio

Code for mem_ref_fun()

template<class R, class T>

mem_ref_fun_t<R,T>

mem_ref_fun(R (T::*pm)())

{

return mem_ref_fun_t<R,T>(pm);

}

Page 71: C++ Training Datascope Lawrence D’Antonio

Code for mem_ref_fun_t()

template<class R, class T>class mem_fun_t: public unary_function<T*,R>

{ R (T::*ptr) ();public: explicit mem_fun_t(R (T::*pm) ()):

ptr(pm) {} R operator()(T &t) const

{ return (t.*ptr)(); }};

Page 72: C++ Training Datascope Lawrence D’Antonio

Is this legal?//Employee, Manager as before

void print_list2(std::set<Employee&>&s){ for_each(s.begin(),s.end(), std::mem_fun_ref(&Employee::print)); }main() { std::set<Employee> t; t.insert(e1); t.insert(e2); t.insert(m); print_list2(t);

Page 73: C++ Training Datascope Lawrence D’Antonio

Not legal.

The Employee class is not sortable. So it cannot be used in a set container. We must overload operator< for the Employee class or give the set a sorting criterion.

Suppose we overload operator< for the Employee class. Will the code be legal then?

Page 74: C++ Training Datascope Lawrence D’Antonio

Sorry. It’s still not legal.

You receive an error message:

“Forming reference to reference”

C++ doesn’t allow a reference to a reference.

Page 75: C++ Training Datascope Lawrence D’Antonio

Can we duplicate this error?

template<class T>class Why {public:

void foo(T &) { }};

main() {Why<int &> w;w.foo();

}

Page 76: C++ Training Datascope Lawrence D’Antonio

Is this legal?

main() {

int x = 4;

int &r = x;

r = 5;

int &rr = r;

return 0;

}

Page 77: C++ Training Datascope Lawrence D’Antonio

Yes, this is legal.

int &r = x; int &rr = r;

doesn’t create a reference to a reference.

Instead, both r and rr are references to x.

Page 78: C++ Training Datascope Lawrence D’Antonio

Is this legal?

void foo(int &a) { a++; }

main() { int x = 4; int &r = x; int &rr = r; foo(r); return 0;}

Page 79: C++ Training Datascope Lawrence D’Antonio

Yes, this is legal.

As before, you are not creating a reference to a reference with the call foo(r).

In foo(int &a), the a will not store a reference to the r, but a reference to the x (which the r references).

Page 80: C++ Training Datascope Lawrence D’Antonio

Is this legal?

main() {

int x = 4;

int &r = x;

int &rr = r;

pair<int,int> p(r,rr);

pair<int &,int &> q(x,x);

pair<int &, int &> s(r,rr);

//...

Page 81: C++ Training Datascope Lawrence D’Antonio

pair<int,int> p(r,rr);This is legal (two ints are created by value and stored in the pair).

pair<int &,int &> q(x,x);Not legal! Reference to reference.

pair<int &, int &> s(r,rr);Not legal! Reference to reference.

Page 82: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A {public: virtual A operator+() { std::cout << "In class A\n"; return *this; }};

class B: public A {public: B operator+() { std::cout << "In class B\n"; return *this; }};

main() { A *p = new A; +(*p); p = new B; +(*p); }

Page 83: C++ Training Datascope Lawrence D’Antonio

No, this is not legal.

A A::operator+() and B B::operator+() are invalid covariant return types.

Page 84: C++ Training Datascope Lawrence D’Antonio

Valid version of exampleclass A {public: virtual A& operator+() { std::cout << "In class A\n";

return *this; }};

class B: public A {public: B& operator+() { std::cout << "In class B\n";

return *this; }};

Page 85: C++ Training Datascope Lawrence D’Antonio

Virtual destructors

Rule of thumb. If a class has any virtual methods then the destructor should be declared virtual.

Page 86: C++ Training Datascope Lawrence D’Antonio

Exampleclass A {public: virtual void f() { std::cout << "A::f\n"; } ~A() { std::cout << "A dies\n"; }};

class B: public A {public: void f() {std::cout << "B::f\n"; } ~B() { std::cout << "B dies\n"; }};

main() { A *p = new B; p->f(); delete p;

Page 87: C++ Training Datascope Lawrence D’Antonio

Output of example

The output is

B::f

A dies This is not correct. The correct version of f() was called (since it is a virtual function). Only the A dtor was called. This is because A’s dtor was not declared virtual.

Page 88: C++ Training Datascope Lawrence D’Antonio

Correct versionclass A {public: virtual void f() { std::cout << "A::f\n"; } virtual ~A() { std::cout << "A dies\n"; }};

class B: public A {public: void f() {std::cout << "B::f\n"; } ~B() { std::cout << "B dies\n"; }};

main() { A *p = new B; p->f(); delete p;

Page 89: C++ Training Datascope Lawrence D’Antonio

This has the correct output, delete p calls the dtor for B then A.

B::fB diesA dies

Note, some programmers recommend that all destructors be virtual. Why not?

Not all classes are meant to be base classes.

Page 90: C++ Training Datascope Lawrence D’Antonio

Virtual constructors

But aren’t virtual constructors illegal? Constructors are unique. For example, you

can’t have a pointer to a constructor. Also, constructors interact with memory

management. So it would seem that a constructor cannot be virtual.

Illegality is in the eye of the offender.

Page 91: C++ Training Datascope Lawrence D’Antonio

“Virtual constructor” example//Base classclass A {public: //Actual constructors A() { std::cout << "A default ctor\n"; } A(const A& a) { std::cout << "A copy ctor\n"; }

//Virtual constructors virtual A* default_ctor() { std::cout << "A virtual ctor\n"; return new A; } virtual A* clone() { std::cout << "A clone\n"; return new A(*this); }

virtual ~A() { std::cout << "A is dead\n"; }};

Page 92: C++ Training Datascope Lawrence D’Antonio

Example part 2//Derived classclass B: public A {public: //Actual constructors B() { std::cout << "B default ctor\n"; } B(const B& b) { std::cout << "B copy ctor\n"; }

//Virtual constructors virtual B* default_ctor() { std::cout << "B virtual ctor\n"; return new B; } virtual B* clone() { std::cout << "B clone\n"; return new B(*this); }

virtual ~B() { std::cout << "B is dead\n"; }};

Page 93: C++ Training Datascope Lawrence D’Antonio

Example part 3void test(A *pa) { A* pa2 = pa->default_ctor(); A *pa3 = pa->clone();} main() { A a; B b; test(&a); test(&b); return 0;}

Page 94: C++ Training Datascope Lawrence D’Antonio

Example outputA default ctorA default ctorB default ctorA virtual ctorA default ctorA cloneA copy ctorB virtual ctorA default ctorB default ctorB cloneA default ctorB copy ctorB is deadA is deadA is dead

Page 95: C++ Training Datascope Lawrence D’Antonio

Virtual function table

How are virtual functions implemented in C++?

For each virtual function in a class, the compiler takes the name of the function and converts it into an index into a table, called the virtual function table (or vtable).

Each class with virtual functions has its own vtable.

Page 96: C++ Training Datascope Lawrence D’Antonio

Virtual function table 2

The vtable has pointers to the virtual functions for that class.

If a derived class has overridden an inherited virtual function then the derived class vtable replaces the base class function with that of the derived class.

Page 97: C++ Training Datascope Lawrence D’Antonio

Vtable example

class B1{ public: void f0() {} virtual void f1() {}}; class B2{ public: virtual void f2() {}};

Page 98: C++ Training Datascope Lawrence D’Antonio

Vtable example part 2

class D : public B1, public B2

{

public:

virtual void d() {}

virtual void f2() {}

};

Page 99: C++ Training Datascope Lawrence D’Antonio

Vtable

Using the g++ command

–fdump-class-hierarchy We get the following vtables

Page 100: C++ Training Datascope Lawrence D’Antonio

B1

Class B1

size=4 align=4

base size=4 base align=4

B1 (0xfefe26c0) 0 nearly-empty

vptr=((&B1::_ZTV2B1) + 8u)

Page 101: C++ Training Datascope Lawrence D’Antonio

Vtable for B1

Vtable for B1

B1::_ZTV2B1: 3u entries

0 0u

4 (int (*)(...))(&_ZTI2B1)

8 B1::f1

Page 102: C++ Training Datascope Lawrence D’Antonio

B2

Class B2

size=4 align=4

base size=4 base align=4

B2 (0xfefe2d40) 0 nearly-empty

vptr=((&B2::_ZTV2B2) + 8u)

Page 103: C++ Training Datascope Lawrence D’Antonio

Vtable for B2

Vtable for B2

B2::_ZTV2B2: 3u entries

0 0u

4 (int (*)(...))(&_ZTI2B2)

8 B2::f2

Page 104: C++ Training Datascope Lawrence D’Antonio

D

Class D size=8 align=4 base size=8 base align=4D (0xfefe31c0) 0 vptr=((&D::_ZTV1D) + 8u) B1 (0xfefe3200) 0 nearly-empty primary-for D (0xfefe31c0) B2 (0xfefe3240) 4 nearly-empty vptr=((&D::_ZTV1D) + 28u)

Page 105: C++ Training Datascope Lawrence D’Antonio

Vtable for D

Vtable for DD::_ZTV1D: 8u entries0 0u4 (int (*)(...))(&_ZTI1D) 8 B1::f112 D::d 16 D::f220 4294967292u24 (int (*)(...))(&_ZTI1D) 28 D::_ZThn4_N1D2f2Ev

Page 106: C++ Training Datascope Lawrence D’Antonio

What’s the output?

class A {public: virtual int foo() const {return 0; }};

class B: public A { int b;public: B(int x): b(x) {} int foo() const { return b; }};

Page 107: C++ Training Datascope Lawrence D’Antonio

bool bar(A a){ return a.foo() > 100;}

main() { B y(200); if (bar(y))

std::cout << "Hello\n"; else std::cout << "Goodbye\n";

return 0;}

Page 108: C++ Training Datascope Lawrence D’Antonio

In the call bar(y),

y.foo() would return 200, so giving output “Hello”, but instead the output I “Goodbye”. Why is that?

The reason is that function bar() is passed an A by value.

Page 109: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A {public:

A() { val = 0; }virtual ~A() {}

protected:A(int x) { val = x; }

private:int val;

};

class B: public A {public:

B(): A(0) {}A *createA(int x = 0) { return new A(x); }

};

Page 110: C++ Training Datascope Lawrence D’Antonio

This is not legal.

A::A(int) is protected, so it’s not accessible in the function createA().

A child can only access protected members in the parent through a pointer or reference to the child.

Page 111: C++ Training Datascope Lawrence D’Antonio

Protected access rules

A protected nonstatic base class member can be accessed by members and friends of any classes derived from that base class by using one of the following:

A pointer to a directly or indirectly derived class. A reference to a directly or indirectly derived

class. An object of a directly or indirectly derived class

Page 112: C++ Training Datascope Lawrence D’Antonio

Example Given the following classes

class A { protected:

int i; };

class B : public A { friend void f(A*, B*); void g(A*);

};

Page 113: C++ Training Datascope Lawrence D’Antonio

Is this legal?

void f(A* pa, B* pb) {

pa->i = 1;

pb->i = 2;

int A::* point_i = &A::i;

int A::* point_i2 = &B::i;

}

Page 114: C++ Training Datascope Lawrence D’Antonio

pa->i = 1;//Illegal, not accessing protected member //through pointer or reference to derived

pb->i = 2;//Legal, accessing i through a pointer//to a B object

int A::* point_i = &A::i;//Illegal, not accessing protected member //through pointer or reference to derived

int A::* point_i2 = &B::i;//Legal, accessing i through a pointer to //a B

Page 115: C++ Training Datascope Lawrence D’Antonio

Is this legal?

void B::g(A* pa) {

pa->i = 1;

i = 2;

int A::* point_i = &A::i;

int A::* point_i2 = &B::i;

}

Page 116: C++ Training Datascope Lawrence D’Antonio

pa->i = 1;//Illegal, pa is not a pointer to a B

i = 2;//Legal, uses B::this pointer int A::* point_i = &A::i;//Illegal, because i is being used as an//A member

int A::* point_i2 = &B::i; //Legal, because i is being used as a //B member

Page 117: C++ Training Datascope Lawrence D’Antonio

Dynamic binding in Java

In Java there are two kinds of methods. Class (or static) methods use static binding. Instance methods (called by an object) use dynamic binding.

When the Java Virtual Machine (JVM) invokes a class method, it uses the type of the object reference.

Page 118: C++ Training Datascope Lawrence D’Antonio

Dynamic binding in Java 2

When the JVM invokes an instance method, it selects the method based on the actual class of the calling object.

JVM uses instructions invokestatic, invokevirtual to invoke these methods.

Java is dynamically linked, so that references to methods are initially symbolic.

Page 119: C++ Training Datascope Lawrence D’Antonio

Dynamic binding in Java 3

Symbolic references include the class name, method name, and method descriptor (the return type and number and types of arguments).

To resolve a symbolic reference, JVM locates the method and replaces the symbolic reference with a direct reference.

Page 120: C++ Training Datascope Lawrence D’Antonio

Dynamic binding in Java 4

During the resolution process the JVM undertakes verification. It checks that the method actually exists, that the associated class can legally access the method (e.g., is it private?).

If the verification fails, the JVM throws an exception.

Page 121: C++ Training Datascope Lawrence D’Antonio

Dynamic binding in Java 5

After the method is resolved the JVM invokes it. For instance methods, it expects the object reference to be on the stack, together with the method’s arguments.

The JVM then creates a new stack frame for the method.

Page 122: C++ Training Datascope Lawrence D’Antonio

Java Examplepublic class Animal {

public void eat() { System.out.println("I eat like a generic Animal."); }

public static void main(String[] args) { Animal[] anAnimal = new Animal[4];

anAnimal[0] = new Animal(); anAnimal[1] = new Wolf(); anAnimal[2] = new Fish(); anAnimal[3] = new OtherAnimal(); for (int i = 0; i < 4; i++)

anAnimal[i].eat(); }

}

Page 123: C++ Training Datascope Lawrence D’Antonio

Java Example 2public class Wolf extends Animal {

public void eat() { System.out.println("I eat like a wolf!"); }

}

public class Fish extends Animal { public void eat() { System.out.println("I eat like a fish!"); }

}

public class OtherAnimal extends Animal { }

Page 124: C++ Training Datascope Lawrence D’Antonio

Output

I eat like a generic animal.

I eat like a wolf!

I eat like a fish!

Page 125: C++ Training Datascope Lawrence D’Antonio

Abstract Classes

An abstract class is one that cannot be instantiated (i.e., you cannot declare variables of this type). This is in contrast to concrete classes.

Abstract classes can be seen as defining a protocol or interface for derived classes.

The protocol consists of a set of operations that derived classes must support.

Page 126: C++ Training Datascope Lawrence D’Antonio

Abstract classes 2

In C++, pure virtual functions define the interface that derived classes must support.

A derived class must implement the pure virtual functions it inherits, or else the derived class itself is abstract.

Page 127: C++ Training Datascope Lawrence D’Antonio

Abstract classes 3

So there are two ways that a class may be abstract. It is a base class with pure virtual functions It is a derived class that inherits pure virtual

functions but doesn’t implement all of them.

Traits are an alternative to abstract classes. Traits are building blocks for classes.

Page 128: C++ Training Datascope Lawrence D’Antonio

Abstract classes 4

Terminology problem. In C++ an abstract class can have data

and defined methods. But pure virtual functions cannot have a definition.

An interface class does not have any concrete features.

Page 129: C++ Training Datascope Lawrence D’Antonio

Example of interface class

class Character_device {

public:

virtual int open(int opt) = 0;

virtual int close(int opt) = 0;

virtual int read(char *p, int n) = 0;

virtual int ioctl(int filedes, int request, ...) = 0;

virtual ~Character_device() {}

};

Page 130: C++ Training Datascope Lawrence D’Antonio

Comments on example

In this example, particular drivers will be classes derived from Character_device.

The abstract base class creates a protocol for all drivers.

Other code will manipulate drivers through that interface.

Page 131: C++ Training Datascope Lawrence D’Antonio

Is this legal?#include <iostream>

class A {public: virtual ~A()=0; virtual void f()=0;};

class B: public A {public: void f() { std::cout << "Concrete\n"; }};

main() { A *p = new B; p->f();}

Page 132: C++ Training Datascope Lawrence D’Antonio

Not legal!

You get a linker error. Why?

The linker complains that there is no definition for A::~A (which is a pure virtual!).

Page 133: C++ Training Datascope Lawrence D’Antonio

So is this legal?#include <iostream>

class A {public: virtual ~A()=0 {}; virtual void f()=0;};

class B: public A {public: void f() { std::cout << "Concrete\n"; }};

main() { A *p = new B; p->f();}

Page 134: C++ Training Datascope Lawrence D’Antonio

No, this is not legal!

This is a compiler error. You can’t define a pure virtual function!

Page 135: C++ Training Datascope Lawrence D’Antonio

Correct version#include <iostream>

class A {public: virtual ~A()=0; virtual void f()=0;};

A::~A() {}

class B: public A {public: void f() { std::cout << "Concrete\n"; }};

Page 136: C++ Training Datascope Lawrence D’Antonio

Pure virtual constructors

Suppose we have an abstract Shape class

class Shape {

public:

//virtual copy constructor

virtual Shape *clone() const = 0;

virtual ~Shape() { };

};

Page 137: C++ Training Datascope Lawrence D’Antonio

Pure virtual constructors 2

In derived classes,

class Circle : public Shape {

public:

Circle *clone() const

{ return new Circle(*this); }

};

Page 138: C++ Training Datascope Lawrence D’Antonio

Pure virtual constructors 3

class Triangle : public Shape {

public:

Triangle *clone() const

{ return new Triangle(*this); }

};

Page 139: C++ Training Datascope Lawrence D’Antonio

Pure virtual constructors 4class Bob {

Shape * p_;public:

Bob(Shape *p): p_(p) { //Check p != 0 }~Bob() { delete p_; }

Bob(const Bob &b): p_(b.p_->clone()) { }Bob &operator=(const Bob &b) {

if (this != &b) {delete p_;p_ = b.p_->clone();

}return *this;

}};

Page 140: C++ Training Datascope Lawrence D’Antonio

Design problem

This is an example in Stroustrup’s The C++ Programming Language.

Problem: provide a way for a program to get an integer value from a user.

Create a base class Ival_box that defines a range of allowed input values.

Ival_box can be used by the program to get and set the value and prompt the user.

Page 141: C++ Training Datascope Lawrence D’Antonio

Design problem 2

The design must account for the fact that there will be very many different types of Ival_boxes.

Stroustrup recommends a “virtual user-interface system”.

The application is isolated from particular user-interface systems.

Page 142: C++ Training Datascope Lawrence D’Antonio

Design problem 3class Ival_box {protected: int val; int high low; bool changed;public: Ival_box(int ll, int hh) { changed = false; val = low = ll; high = hh; } virtual int get_value() { changed = false; return val; } virtual void set_value (int i) { changed = true; val = i; } virtual void prompt() { } virtual bool was_changed() const { return changed; }};

Page 143: C++ Training Datascope Lawrence D’Antonio

Design problem 4 Polymorphism using Ival_box

void interact(Ival_box *p) { p->prompt(); int i = pb->get_value(); if (pb->was_changed()) { //New value, do something } else { //Old value, do something }}

Page 144: C++ Training Datascope Lawrence D’Antonio

Types of Ival_boxes

Ival_box

Ival_slider Ival_dial

Popup_Ival_slider Flashing_Ival_slider

Page 145: C++ Training Datascope Lawrence D’Antonio

How do we get the graphics?

User-interface systems provide class(es) which define what it means to be an object on the screen.

Suppose we use the user-interface class BB_Window (bought from Big Bucks Inc.).

We could make each of our Ival_boxes a kind of BB_Window.

Page 146: C++ Training Datascope Lawrence D’Antonio

Implementation

BB_Window

Ival_box

Ival_slider Ival_dial

Popup_Ival_slider Flashing_Ival_slider

Page 147: C++ Training Datascope Lawrence D’Antonio

Is this a good design?

The concept of an Ival_box doesn’t depend on BB_Window.

The specific user-interface is an implementation detail. Should it be part of the overall design?

One of the mantras of OOP is “Separate interface from implementation.”

Page 148: C++ Training Datascope Lawrence D’Antonio

Is this a good design? Part 2

Deriving from BB_Window allows our code to use the facilities of BB_Window (which might be a good thing).

But, future changes to BB_Window may force us to recompile (or even rewrite) our code (which is a bad thing).

Page 149: C++ Training Datascope Lawrence D’Antonio

Is this a good design? Part 3

What if the program must run in a mixed environment which uses several different types of user interface systems? Our design is wired in to the BB-Window system?

We might want to use CW_Window (Compiler Whizzes), IB_Window (Imperial Bananas), or LS_Window (Liberated Software).

Page 150: C++ Training Datascope Lawrence D’Antonio

Is this a good design? Part 4

Another problem: every derived class inherits the data declared in Ival_box.

But do the slider or dial controls really need to store a value? The function get_value() calculate the value from the position of the control.

The problem is that you cannot disinherit features of the parent.

Page 151: C++ Training Datascope Lawrence D’Antonio

New Design

The user-interface system should be an implementation detail hidden from users.

No recompilation of the Ival_box family should be required after a change in the user-interface system.

Ival_boxes for different user interface systems should be able to coexist.

Page 152: C++ Training Datascope Lawrence D’Antonio

How do we implement?

Where do we include BB_Window? Making BB_Window a base for Ival_box

has problems mentioned before. Making BB_Window a data member of Ival_box will not work. You cannot override member functions of a data member.

Could have a data member BB_Window* in Ival_box.

Page 153: C++ Training Datascope Lawrence D’Antonio

Better Solution Make Ival_box an abstract base class (ABC).

class Ival_box {public:virtual int get_value() = 0;virtual void set_value (int i) = 0;virtual void prompt() = 0;virtual bool was_changed() const = 0;virtual ~Ival_box {}

};

Page 154: C++ Training Datascope Lawrence D’Antonio

Types of Ival_boxes

class Ival_slider: public Ival_box, protected BB_Window {

public:

//Override functions from Ival_box

protected:

//Override functions from BB_Window

private:

//Data members specific to slider

};

Page 155: C++ Training Datascope Lawrence D’Antonio

New Hierarchy

BB_Window Ival_box LS_Window

Ival_slider Ival_dial

Popup_Ival_slider Flashing_Ival_slider

Page 156: C++ Training Datascope Lawrence D’Antonio

Does this solve all our problems?

No, we still have problems trying to have different user-interface systems coexist in the design.

In other words, one cannot simultaneously have an Ival_slider for BB_Window and LS_Window.

Page 157: C++ Training Datascope Lawrence D’Antonio

For example

The following is not legal.

class Ival_slider: public Ival_box, protected BB_Window {};

class Ival_slider: public Ival_box, protected LS_Window {};

Page 158: C++ Training Datascope Lawrence D’Antonio

A solution

Have each implementation of Ival_slider define a different class.

class BB_ival_slider: public Ival_box, protected BB_Window {};

class LS_ival_slider: public Ival_box, protected LS_Window {};

Page 159: C++ Training Datascope Lawrence D’Antonio

Class hierarchy

BB_Window Ival_box LS_Window

BB_ival_slider LS_ival_slider

Page 160: C++ Training Datascope Lawrence D’Antonio

Better solution

We can further insulate our design from implementation details.

We can make Ival_slider an abstract class.

Then we can derive specific sliders from the abstract class.

Page 161: C++ Training Datascope Lawrence D’Antonio

New class design

class Ival_box {}; //ABCclass Ival_slider: public Ival_box {}; //ABC

//Implemetation classesclass BB_ival_slider: public Ival_slider,

protected BB_window {};

class LS_ival_slider: public Ival_slider, protected LS_Window {};

Page 162: C++ Training Datascope Lawrence D’Antonio

New class hierachy

Ival_box

BB_Window Ival_slider LS_Window

BB_ival_slider LS_ival_slider

Page 163: C++ Training Datascope Lawrence D’Antonio

Even better hierarchy

BB_Window Ival_box LS_Window

BB_slider Ival_slider LS_slider

BB_ival_slider LS_ival_slider

Page 164: C++ Training Datascope Lawrence D’Antonio

Bridge Pattern

GoF definition The purpose of the Bridge Pattern is to

“De-couple an abstraction from its implementation so that the two can vary independently.”

Page 165: C++ Training Datascope Lawrence D’Antonio

Bridge Pattern Diagram

Page 166: C++ Training Datascope Lawrence D’Antonio

Bridge and the OAOO rule

The bridge pattern exemplifies the once and only once rule.

This rule, stated by Kent Beck, asserts that “each and every declaration of behavior should appear once and only once.”

Page 167: C++ Training Datascope Lawrence D’Antonio

More on OAOO

One should eliminate duplicated behavior.

If duplicate behavior appears, merge them or replace multiple implementations with a unifying abstraction.

This reflects the goal of making code as simple as possible.

Page 168: C++ Training Datascope Lawrence D’Antonio

Bridge Pattern Analysis

Look for variations in abstractions of a concept

Look for variations in implementations of these concepts

Page 169: C++ Training Datascope Lawrence D’Antonio

Commonality vs. Variability

From Jim Coplien, Multi-Paradigm Design for C++

“Commonality analysis is the search for common elements that helps us understand how family elements are the same.”

“Commonality analysis seeks structure that is unlikely to change over time.”

Page 170: C++ Training Datascope Lawrence D’Antonio

Commonality vs. Variability 2

“ Variability analysis captures structure that is likely to change.”

“Variability analysis makes sense only in terms of the context defined by the associated commonality analysis.”

“From an architectural perspective, commonality analysis gives the architecture its longevity; variability analysis drives its fitness for use.”

Page 171: C++ Training Datascope Lawrence D’Antonio

Bridge Pattern Example

Suppose we are writing a program that will allow the user to draw basic shapes. There are two different drawing programs that may be used, DP1 and DP2. Each provide the necessary draw functions.

We want a design which connects the shape concepts to the drawing programs.

Page 172: C++ Training Datascope Lawrence D’Antonio

Class Hierarchy

Rectangle

+draw()

#drawline() = 0

V1_Rectangle

#drawline()

V2_Rectangle

#drawline()

DP1

+draw()

DP2

+draw()

Page 173: C++ Training Datascope Lawrence D’Antonio

Class Definition

class Rectangle {public:

void draw();protected:

virtual voiddrawline(Point,Point)= 0;

private:Point _ne, _se, _nw, _sw;

};

Page 174: C++ Training Datascope Lawrence D’Antonio

Class Definition 2

void Rectangle::draw() {

drawline(_ne,_se);

drawline(_se,_sw);

drawline(_sw,_nw);

drawline(_nw,_ne);

};

Page 175: C++ Training Datascope Lawrence D’Antonio

Class Definition 3

class V1_Rectangle {

protected:

void drawline(Point a, Point b)

{ _imp->draw_line(a,b); }

private:

DP1* _imp;

};

Page 176: C++ Training Datascope Lawrence D’Antonio

Class Definition 4

class V2_Rectangle {

protected:

void drawline(Point a, Point b)

{ _imp->draw_line(a,b); }

private:

DP2* _imp;

};

Page 177: C++ Training Datascope Lawrence D’Antonio

Is this a good design?

This design illustrates combinatorial explosion.

If there are m different types of Shapes and n drawing programs this design will require m + n + mn different classes to be defined.

The ideal design, which completely separates abstraction from implementation would only require m + n different classes.

Page 178: C++ Training Datascope Lawrence D’Antonio

More design flaws

The abstraction and implementation are tightly coupled.

Each type of shape must know which drawing program it’s using.

There is redundancy in the design. The code for V1_Rectangle and V2_Rectangle is essentially the same.

Page 179: C++ Training Datascope Lawrence D’Antonio

Alternative design

Try to reduce the coupling between abstraction and design.

Have each implementation define its own inheritance scheme.

Page 180: C++ Training Datascope Lawrence D’Antonio

New Class Hierarchy

+draw()

Shape

#drawLine()#drawCircle()

V1_Shape

#drawLine()#drawCircle()

V2_Shape

+draw()

V1_Rectangle

+draw()

V1_Circle

+draw()

V2_Rectangle

+draw()

V2_Circle

+draw_line()

DP1

+draw_line()

DP2

-Implementor

11

-Implementor

1 1

Page 181: C++ Training Datascope Lawrence D’Antonio

How good is this design?

There is no longer as much coupling in the design. Each drawing program has it’s own shapes.

There is still redundancy. V1_Rectangle and V2_Rectangle will have the same draw() method.

There is still combinatorial explosion in classes when new shapes or drawing programs are added.

Page 182: C++ Training Datascope Lawrence D’Antonio

How can the design be improved?

Use the following principles.

Find what varies and encapsulate it.

Favor composition over inheritance.

Page 183: C++ Training Datascope Lawrence D’Antonio

How to apply these principles

What varies in this situation? There are different shapes and different

drawing programs. Encapsulate shapes, who know how to

draw themselves. Encapsulate drawing methods for lines

and circles, all draw() functions will use these basic methods.

Page 184: C++ Training Datascope Lawrence D’Antonio

What varies?

There are two types that vary in this system. The type of Shape and the type of drawing program.

Drawing

drawLine( )drawCircle( )

Shape

draw( )

Page 185: C++ Training Datascope Lawrence D’Antonio

Represent variations

Particular variations in each type.

Shape

draw( )

Drawing

drawLine( )drawCircle( )

Rectangle

draw( )

Circle

draw( ) V1Drawing

drawLine( )drawCircle( )

V2Drawing

drawLine( )drawCircle( )

Page 186: C++ Training Datascope Lawrence D’Antonio

Tie classes together

How should the Shape and Drawing types be connected?

Prefer composition over inheritance.

Should Shape use Drawing or Drawing use Shape?

Page 187: C++ Training Datascope Lawrence D’Antonio

Tie classes together 2

What if Drawing used Shape? Then the drawing program would need to

know about the different types of Shapes. They would need to know specific details

of the Shapes. This would break encapsulation for the

Shape class.

Page 188: C++ Training Datascope Lawrence D’Antonio

Tie classes together 3

What if Shape used Drawing? Shape classes would not need to know

details of the drawing programs. Shape classes only need to use the

interface of the drawing program. They don’t need to know the specific program being used.

Page 189: C++ Training Datascope Lawrence D’Antonio

Tie classes together 4

Rectangle

draw( )

Circle

draw( )

V1Drawing

drawLine( )drawCircle( )

V2Drawing

drawLine( )drawCircle( )

Drawing

drawLine( )drawCircle( )

Shape

draw( )

Page 190: C++ Training Datascope Lawrence D’Antonio

Fill in the details

Each concrete drawing class will be associated to a particular drawing program.

The Shape class will have protected member functions drawLine() and drawCircle().

Page 191: C++ Training Datascope Lawrence D’Antonio

Final Design

Rectangle

draw( )

Circle

draw( )

V1Drawing

drawLine( )drawCircle( )

V2Drawing

drawLine( )drawCircle( )

Drawing

drawLine( )drawCircle( )

Shape

draw( )drawLine( )drawCircle( )

DP2

draw_a_Line( )draw_a_Circle( )

DP2

draw_a_Line( )draw_a_Circle( )

Page 192: C++ Training Datascope Lawrence D’Antonio

Conclusions

There are only 3 objects being used at a time.

A Shape object (all of which look identical in operation to the client).

A Drawing object (all of which look identical in operation to the Shape).

An actual drawing program (the Drawing object must use a specific drawing program).

Page 193: C++ Training Datascope Lawrence D’Antonio

Sample Codeclass Shape {private: Drawing *_dp;protected: void drawLine(double x1, double y1,

double x2, double y2) const { _dp->drawLine(x1,y1,x2,y2); }

void drawCircle(double x, double y, double r) const

{ _dp->drawCircle(x,y,r); }public: Shape(Drawing *dp): _dp(dp) {} virtual void draw() const = 0;

virtual ~Shape() { delete _dp; }};

Page 194: C++ Training Datascope Lawrence D’Antonio

Sample Code 2class Rectangle: public Shape {private: double _x1, _y1, _x2, _y2;public: Rectangle(Drawing *dp, double x1, double y1, double x2, double y2): Shape(dp), _x1(x1), _y1(y1),

_x2(x2), _y2(y2) {}

void draw() const { drawLine(x1,y1,x2,y1); drawLine(x2,y1,x2,y2); drawLine(x2,y2,x1,y2); drawLine(x1,y2,x1,y1); }};

Page 195: C++ Training Datascope Lawrence D’Antonio

Sample Code 3

class Circle: public Shape {private: double _x, _y, _r;public: Circle(Drawing *dp,

double x, double y, double r): Shape(dp), _x(x), _y(y), _r(r) {} void draw() const { drawCircle(_x,_y,_r); }};

Page 196: C++ Training Datascope Lawrence D’Antonio

Sample Code 4

class Drawing {public: virtual void drawLine( double x1, double y1, double x2, double y2) const = 0;

virtual void drawCircle( double x, double y, double r)

const = 0;};

Page 197: C++ Training Datascope Lawrence D’Antonio

Sample Code 5

class V1Drawing: public Drawing {public: void drawLine(double x1, double y1, double x2, double y2) const { DP1::draw_a_line(x1,y1,x2,y2); } void drawCircle(double x, double y,

double r) const { DP1::draw_a_circle(x,y,r); }};

Page 198: C++ Training Datascope Lawrence D’Antonio

Sample Code 6

class V2Drawing: public Drawing {public: void drawLine(double x1, double y1, double x2, double y2) const { DP2::draw_a_line(x1,y1,x2,y2); } void drawCircle(double x, double y,

double r) const { DP2::draw_a_circle(x,y,r); }};

Page 199: C++ Training Datascope Lawrence D’Antonio

Sample Code 7

class DP1 {public: static void draw_a_line( double x1, double y1, double x2, double y2) const;

static void draw_a_circle( double x, double y, double r) const;};

Page 200: C++ Training Datascope Lawrence D’Antonio

Sample Code 8

class DP2 {public: static void draw_a_line( double x1, double y1, double x2, double y2) const;

static void draw_a_circle( double x, double y, double r) const;};

Page 201: C++ Training Datascope Lawrence D’Antonio

Bjarne’s advice column

Avoid type fields. Use pointers and references to avoid

slicing. Use abstract classes to focus design on

the provision of clean interfaces. Use abstract classes to minimize

interfaces.

Page 202: C++ Training Datascope Lawrence D’Antonio

Bjarne’s advice column 2

Use abstract classes to keep implementation details out of interfaces.

Use virtual functions to allow new implementations to be added without affecting user code.

Use abstract classes to minimize recompilation of user code.

Page 203: C++ Training Datascope Lawrence D’Antonio

Bjarne’s advice column 3

Use abstract classes to allow alternative implementations to coexist.

A class with a virtual function should have a virtual destructor.

An abstract class typically doesn’t need a constructor.

Keep the representations of distinct concepts distinct.

Page 204: C++ Training Datascope Lawrence D’Antonio

Multiple Inheritance

Multiple inheritance consists in a class having more than one direct base class.

MI allows the programmer to combine independent concepts represented by different classes into a composite concept represented by the derived class.

A class with multiple parents is called a join class.

Page 205: C++ Training Datascope Lawrence D’Antonio

Multiple Inheritance 2

A class may only be used once as a direct base class but may be used multiple times as an indirect base class.

If there is more than one path from an ancestor class down to a descendent then there are possible problems.

This is called the common root problem.

Page 206: C++ Training Datascope Lawrence D’Antonio

Multiple Inheritance 3

What are the issues associated with the common root problem?

Duplicate data. Any data inherited from a common root is stored as many times in the child as there paths from the root to the child.

Ambiguities. Multiple versions of the same function can be inherited.

Page 207: C++ Training Datascope Lawrence D’Antonio

Why use multiple inheritance?

Feature Intersection: A child has features of two parents. The child class may be considered the intersection of the parents.

For example. The class iostream combines features of istream and ostream.

Page 208: C++ Training Datascope Lawrence D’Antonio

Mixin Classes

A mixin classes combines a specific type of feature with a subclass of some inheritance scheme.

Mixin classes are generally not intended to be standalone.

For example, take any type of ice cream and combine it with some garnish (nuts, sprinkles, sundae sauce). The garnish is a mixin (not meant to be eaten by itself).

Page 209: C++ Training Datascope Lawrence D’Antonio

Mixin Classes 2

Mixin classes define another form of multiple inheritance.

A class that is already part of an inheritance scheme uses the mixin class as another parent in order to add some specific feature.

Flavors was the first OOPL that used multiple inheritance and mixin classes.

Page 210: C++ Training Datascope Lawrence D’Antonio

MI Memory Layout

Suppose we have the class hierarchy

A A A

B C

D

Page 211: C++ Training Datascope Lawrence D’Antonio

MI Memory Layout

A part of D

A part of B

B part of D

A part of C

C part of D

D itself

Page 212: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {public: virtual void foo()

{ std::cout << "A::foo()\n"; }};

class B: public A {};

class C: public A {public: void foo() { std::cout << "C::foo()\n"; }};

Page 213: C++ Training Datascope Lawrence D’Antonio

Example part 2

class D: public B, public C { };

main() { A *p;

p = new A; p->foo();

p = new B;p->foo();

Page 214: C++ Training Datascope Lawrence D’Antonio

Example part 3

p = new C;p->foo();

p = new D;p->foo();

B *q = new D;q->foo();

Page 215: C++ Training Datascope Lawrence D’Antonio

p = new A;

p->foo();

// Legal, p is an A pointer.

// Output is: A::foo()

p = new B;

p->foo();

// Legal, p points at the A part of B

// Output is: A::foo()

Page 216: C++ Training Datascope Lawrence D’Antonio

p = new C;

p->foo();

// Legal, p points at the A part of C

// Output is: C::foo()

p = new D;

p->foo();

// Illegal, A is an ambiguous base of D

Page 217: C++ Training Datascope Lawrence D’Antonio

B *q = new D;

q->foo();

// Legal, q points at the B part of D

// Output is: A::foo()

Page 218: C++ Training Datascope Lawrence D’Antonio

Is this legal?

In the previous example

D d;

d.foo();

Page 219: C++ Training Datascope Lawrence D’Antonio

D d;

// Legal, A is an ambiguous base

// but this is ok as long as A

// is not used

d.foo();

// Illegal, this is ambiguous

// Could be A::foo() or C::foo()

Page 220: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {public: virtual void foo()

{ std::cout << "A::foo()\n"; }};

class B: public A {};

class C: public A {public: void foo() { std::cout << "C::foo()\n"; }};

Page 221: C++ Training Datascope Lawrence D’Antonio

Example part 2

class D: public A, public B, public C { };

main() {

D d;

d.A::foo();

}

Page 222: C++ Training Datascope Lawrence D’Antonio

First, the declaration

D d;

Gets a warning: direct base 'A' inaccessible in 'D' due to ambiguity.

Next,

d.A::foo()

Is ambiguous. Compiler states that 'A‘ is an ambiguous base of 'D‘.

Page 223: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A {public: virtual void foo()

{ std::cout << "A::foo()\n"; }};

class B: public virtual A {};

class C: public virtual A {public: void foo() { std::cout << "C::foo()\n"; }};

Page 224: C++ Training Datascope Lawrence D’Antonio

Example part 2

class D: public A, public B, public C { };

main() {

D d;

d.A::foo();

}

Page 225: C++ Training Datascope Lawrence D’Antonio

First, the declaration

D d;

Gets a warning: direct base 'A' inaccessible in 'D' due to ambiguity.

Next,

d.A::foo()

Is ambiguous. Compiler states that 'A‘ is an ambiguous base of 'D‘.

Page 226: C++ Training Datascope Lawrence D’Antonio

Is this legal?

In the previous example, if we replace:

p = new D;

p->foo();

byp = static_cast<C*>(new D);

p->foo();

Page 227: C++ Training Datascope Lawrence D’Antonio

This is legal.

The assignment

p = static_cast<A*>(static_cast<C*>(new D));

makes p point at the A part of the C part of D.

The call p->foo() has output

C::foo()

Page 228: C++ Training Datascope Lawrence D’Antonio

Is this legal?class A {public: void f(int) { std::cout << "A::f(int)\n"; } void f(char) { std::cout << "A::f(char)\n"; }};

class B {public: void f(double) { std::cout << "B::f(double)\n"; }};

class AB: public A, public B {public: void f(char) { std::cout << "AB::f(char)\n"; } void f(AB) { std::cout << "AB::f(AB)\n"; }};

Page 229: C++ Training Datascope Lawrence D’Antonio

Example part 2

void g(AB &ab){ ab.f(1); ab.f('a'); ab.f(2.0); ab.f(ab);} main(){ AB ab; g(ab); }

Page 230: C++ Training Datascope Lawrence D’Antonio

void g(AB &ab)

{

ab.f(1);

//Legal, but calls AB::f(char)

ab.f('a');

//Legal, but calls AB::f(char)

ab.f(2.0);

//Legal, but calls AB::f(char)

ab.f(ab);

//Legal, calls AB::f(AB)

}

Page 231: C++ Training Datascope Lawrence D’Antonio

Output of example

AB::f(char)

AB::f(char)

AB::f(char)

AB::f(AB)

Page 232: C++ Training Datascope Lawrence D’Antonio

What’s the output?

class A {public: void f(int) { std::cout << "A::f(int)\n"; } void f(char) { std::cout << "A::f(char)\n"; }};

class B {public: void f(double) { std::cout << "B::f(double)\n"; }};

Page 233: C++ Training Datascope Lawrence D’Antonio

Example part 2

class AB: public A, public B {public:

using A::f;using B::f;

void f(char) { std::cout << "AB::f(char)\n"; }

void f(AB) { std::cout << "AB::f(AB)\n"; }

};

Page 234: C++ Training Datascope Lawrence D’Antonio

Example part 3

void g(AB &ab){ ab.f(1); ab.f('a'); ab.f(2.0); ab.f(ab);} main(){ AB ab; g(ab); }

Page 235: C++ Training Datascope Lawrence D’Antonio

Output of example

A::f(int)

AB::f(char)

B::f(double)

AB::f(AB)

Page 236: C++ Training Datascope Lawrence D’Antonio

Common root problem

What’s the problem with multiple inheritance?

Common root problem

A

B C

D

Page 237: C++ Training Datascope Lawrence D’Antonio

Common root problem 2

As we saw in the previous example, class D inherits two copies of A.

Hence D has the problem of having A as an ambiguous base.

How to solve the common root problem? The virtual base class mechanism insures

that D will only have one copy of A.

Page 238: C++ Training Datascope Lawrence D’Antonio

Inheritance Lattice

Page 239: C++ Training Datascope Lawrence D’Antonio

Lattice search

How does one search the inheritance lattice in order to find some attribute or method not defined locally?

There may be several paths in the lattice that must be searched to locate a particular feature.

Page 240: C++ Training Datascope Lawrence D’Antonio

Lattice search 2

What search algorithm should be used?

The search method may uncover features of the same name in different classes in the lattice. This is a name clash.

Page 241: C++ Training Datascope Lawrence D’Antonio

Name clash resolution

One alternative. When searching the inheritance lattice, if a name clash occurs then flag that as an error and terminate the search.

Second alternative. Permit qualified message passing. Inherited methods are qualified with the name of the class in which it is defined. This is a type of renaming.

Page 242: C++ Training Datascope Lawrence D’Antonio

Approaches to multiple inheritance

There are four approaches Tree inheritance Graph inheritance Linearized inheritance Prohibition

Page 243: C++ Training Datascope Lawrence D’Antonio

Tree inheritance

Tree inheritance involves labeling nodes when conflicts arise.

A class may inherit more than one copy of a slot, but with different names.

If a slot can be reached via a number of paths in the inheritance lattice, there will be a separate set of slots for each path.

Page 244: C++ Training Datascope Lawrence D’Antonio

Tree inheritance 2

For example, take the diamond diagram.

A

B C

D

Page 245: C++ Training Datascope Lawrence D’Antonio

Tree inheritance 3

Tree inheritance transforms the diamond into the following diagram.

A A

B C

D

Page 246: C++ Training Datascope Lawrence D’Antonio

Tree inheritance 4

This is now a tree.

Unfortunately there still exists the problem of duplicated data.

There are examples where tree inheritance works poorly.

Page 247: C++ Training Datascope Lawrence D’Antonio

Tree inheritance 5

Example. Suppose there is a Point class. Points can be moved (they store x and y

coordinates). Derived from the Point class are classes HistoryPoint and BoundedPoint.

HistoryPoint records all movements of the Point.

BoundedPoint only allows the point to move within fixed boundaries.

Page 248: C++ Training Datascope Lawrence D’Antonio

Tree inheritance 6

What if we want a BoundedHistoryPoint?

This class will inherit a move method from each parent. Neither move method is appropriate for BoundedHistoryPoint.

The two parents cannot be combined using tree inheritance. So this type of inheritance is insufficient.

Page 249: C++ Training Datascope Lawrence D’Antonio

Graph inheritance

Here the inheritance lattice is treated as a graph.

For example, to invoke all the definitions of a certain operation in the inheritance graph, each class can define an operation on each of its parents and then perform any local computation.

Page 250: C++ Training Datascope Lawrence D’Antonio

Graph inheritance 2

In many languages supporting graph inheritance there is one set of instance variables for each ancestor class, regardless of how many paths by which the class can be reached.

There is a problem with this approach. It exposes the inheritance structure of the graph.

Page 251: C++ Training Datascope Lawrence D’Antonio

Graph inheritance 3

Namely, encapsulation implies that the way a class is derived should not be visible.

Only immediate parents should be visible to a class.

But graph inheritance typically means that the structure of inheritance is exposed to subclasses.

Page 252: C++ Training Datascope Lawrence D’Antonio

Graph inheritance 4

Also, in using graph inheritance, changing the inheritance of an intermediate class may break a descendant class.

For example, suppose we have:

Z

Y1 Y2

X

Page 253: C++ Training Datascope Lawrence D’Antonio

Graph inheritance 5

Suppose there is a function foo() defined in each of the classes Z, Y2, and X. Suppose that the foo() in X invokes the function foo() in its parents.

Now suppose that the class Y2 is modified so that it inherits from Z and uses the function foo() defined in Z.

Page 254: C++ Training Datascope Lawrence D’Antonio

Graph inheritance 6

The function foo() in X then invokes foo() in Z twice.

So a change in the inheritance structure of one class (Y2) breaks one of its child classes (X).

Page 255: C++ Training Datascope Lawrence D’Antonio

Linearized inheritance

Linearized inheritance flattens the inheritance lattice into a linear chain and then searches the chain in order to find slots.

The simplest linearization is to do a depth first transversal.

The problem is that the order of classes in the linear chain is not natural.

Page 256: C++ Training Datascope Lawrence D’Antonio

Linearized inheritance

Problems with linearized inheritance: The linearization process may insert

unrelated classes between related classes in the sorted superclass chain.

The method that is invoked through the linear chain may be sub-optimal. The optimal method may be farther back in the chain and hence hidden by the sub-optimal.

Page 257: C++ Training Datascope Lawrence D’Antonio

Alternatives to MI

Perspectives

Interfaces

Prototypes

Aggregation

Page 258: C++ Training Datascope Lawrence D’Antonio

Perspectives

In languages that implement perspectives, a class may have, in addition to data and methods, a list of perspectives. These are additional ways of looking at objects of a class.

An object may use particular perspectives for its class.

Page 259: C++ Training Datascope Lawrence D’Antonio

Perspectives 2

For example, I am an object of the Professor class.

Some of the perspectives that apply to me: Husband, Traveler, Punk Rock fan

Page 260: C++ Training Datascope Lawrence D’Antonio

Interfaces

In Java and other OOPLs, one can inherit from a single superclass and implementing one or more interfaces.

An interface is not a class, but the specification of data and operations that, when implemented, will achieve the intended meaning of the interface.

Page 261: C++ Training Datascope Lawrence D’Antonio

Interfaces 2 Example

public interface Predator { boolean chasePrey(Prey p); void eatPrey(Prey p);

}

public class Cat implements Predator {public boolean chasePrey(Prey p)

{ // cat behavior } public void eatPrey (Prey p) { // cat behavior }

}

Page 262: C++ Training Datascope Lawrence D’Antonio

Prototypes

In most OOP languages there is a distinction between a class and its objects.

In a prototype language there is no such distinction. Instead everything is a prototype.

SELF, Kevo, and Omega are examples of prototype languages.

Page 263: C++ Training Datascope Lawrence D’Antonio

Prototypes 2

A prototype is a representative example of a concept.

Prototypes are examples of at least one concept.

What is a Datascope programmer? My prototype of a Datascope programmer

is Ralph Ebler.

Page 264: C++ Training Datascope Lawrence D’Antonio

Prototypes 3

Your concept of a Datascope programmer may look a lot different from how Ralph looks.

What do you do? You clone Ralph. I know, this sounds even worse than

having one Ralph.

Page 265: C++ Training Datascope Lawrence D’Antonio

Prototypes 4

The different features of an object (attributes and methods) are known as slots.

The clone can be modified in several ways.

Yes, you can make a new, better version of Ralph.

Page 266: C++ Training Datascope Lawrence D’Antonio

Prototypes 5

Ways of modifying a clone. Add a slot. Remove a slot. Rename a slot. Hide a slot. Show a slot. Redefine a slot.

Page 267: C++ Training Datascope Lawrence D’Antonio

Delegation

When cloning one doesn’t always need to copy methods from the parent to the clone.

Access to the parent’s slots is created through delegation.

Delegation replaces inheritance in prototype languages.

Page 268: C++ Training Datascope Lawrence D’Antonio

Delegation 2

Requests made to objects contain references to slots.

If the slot is not present in an object, the request can be delegated to another object.

Typically requests are delegated to the parents of the object to whom the initial request was made.

Page 269: C++ Training Datascope Lawrence D’Antonio

Delegation 3

If the immediate parent of the object does not contain the slot, the request is passed up the chain of parents.

When the slot is first encountered, the value stored there is returned or the method stored there is invoked.

Page 270: C++ Training Datascope Lawrence D’Antonio

Delegation 4

Prototypes will generally have a slot containing a pointer to its parent.

In some languages the value stored in this slot may be changed, thus dynamically changing an object’s parent.

This is called dynamic inheritance or computed delegation.

Page 271: C++ Training Datascope Lawrence D’Antonio

Delegation 5

Delegation is different from multiple inheritance.

There is a specific chain of parents that is followed in delegation (although that chain may be modified during program execution).

This message passing up the chain of parents acts like single inheritance.

Page 272: C++ Training Datascope Lawrence D’Antonio

Shared behavior

One problem of prototype languages is how to represent shared behavior.

But prototypes are all individuals. Since individuals may share properties in the real world, the representation problem is significant.

SELF uses traits to solve this problem.

Page 273: C++ Training Datascope Lawrence D’Antonio

Traits

Traits are parent objects containing shared behavior.

They act like classes do in a class-based system.

For example, there may be many different Point objects in a system.

A Point traits object contains the common properties of points.

Page 274: C++ Training Datascope Lawrence D’Antonio

Traits 2

For example, each Point object has mutable slots for the x and y coordinates of the Point.

The Point traits object is the parent of each Point. The traits objects contains common methods for handling points (e.g., adding and multiplying points).

Page 275: C++ Training Datascope Lawrence D’Antonio

Aggregation

Complex objects are built out of simpler objects. The complex object is an aggregate.

This is an alternative to inheritance. Objects share code through composition.

Aggregation is based on the idea that components of an object are used either by naming or message passing.

Page 276: C++ Training Datascope Lawrence D’Antonio

Aggregation 2

Naming occurs when one object names the components of another object and refers to these remote components by use of the names.

In order to accomplish this, the aggregate must have access to at least the interface of the component.

Page 277: C++ Training Datascope Lawrence D’Antonio

Aggregation 3

Aggregation by message passing is very similar to delegation.

If the aggregating object wants a component, it sends a message to the object which contains it. The receiving object then returns a message with the desired component.

Page 278: C++ Training Datascope Lawrence D’Antonio

Aggregation vs. Inheritance

Suppose we want a stack class based on a deque.

Suppose the stack inherits from a deque?

class Stack: public Deque {};

Problem: this exposes Stack to potentially harmful public member functions of Deque.

Page 279: C++ Training Datascope Lawrence D’Antonio

Aggregation vs. Inheritance 2

Suppose the stack contains a deque? In Beta,

Stack : (# d: Dequepush : d.enterInFront;pop : d.removeInFront;(* … *)

#)

Page 280: C++ Training Datascope Lawrence D’Antonio

Aggregation vs. Inheritance 3

In this example, aggregation is clearly more natural than inheritance.

In using aggregation the Deque class is not exposed. It is used internally in the Stack methods.

Page 281: C++ Training Datascope Lawrence D’Antonio

Virtual base classes

Notation:

class A {};

class B: public virtual A {};

class C: public virtual A { };

class D: public B, public C { };

Page 282: C++ Training Datascope Lawrence D’Antonio

Virtual base classes 2

Note: One can write either

class B: public virtual A {};

or

class B: virtual public A {};

Page 283: C++ Training Datascope Lawrence D’Antonio

Virtual base classes 3

The effect of declaring A as a virtual base class is that D will only contain one copy of A.

In a sense, it’s as if A has become a direct base class of D.

D is responsible for constructing its A subobject (usually a child is only responsible for constructing its direct base classes).

Page 284: C++ Training Datascope Lawrence D’Antonio

Virtual base classes 4

Since D has only one copy of A, the data of A is not duplicated in D.

When using virtual functions, there must be one function that overrides all others.

There must be an overriding class that is derived from every other overriding class.

Page 285: C++ Training Datascope Lawrence D’Antonio

MI Memory Layout (VBC)

A part of D

B part of D

C part of D

D itself

Page 286: C++ Training Datascope Lawrence D’Antonio

What’s the output?

class A {public: virtual void foo()

{ std::cout << "A::foo()\n"; }};

class B: public virtual A {};

class C: public virtual A {public: void foo() { std::cout << "C::foo()\n"; }};

Page 287: C++ Training Datascope Lawrence D’Antonio

Example part 2

class D: public B, public C { };

main() { A *p;

p = new A; p->foo();

p = new B;p->foo();

Page 288: C++ Training Datascope Lawrence D’Antonio

Example part 3

p = new C;p->foo();

p = new D;p->foo();

D d;d.foo();

Page 289: C++ Training Datascope Lawrence D’Antonio

Example part 4

B *q = new D;

q->foo();

Page 290: C++ Training Datascope Lawrence D’Antonio

Output

A::foo()

A::foo()

C::foo()

C::foo()

C::foo()

C::foo()

Page 291: C++ Training Datascope Lawrence D’Antonio

What’s the output?

Suppose we make the following change.

class A {

public:

void foo()

{ std::cout << "A::foo()\n"; }

};

Page 292: C++ Training Datascope Lawrence D’Antonio

Output

A::foo()

A::foo()

A::foo()

A::foo()

C::foo()

A::foo()

Page 293: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A { private: int x;public: A(int a): x(a) {}};

class B: public virtual A {public: B(int b): A(b+1) {}};

Page 294: C++ Training Datascope Lawrence D’Antonio

Example part 2

class C: public virtual A {public: C(int c): A(c+2) {}};

class D: public B, public C {public: D(int d): B(d-1), C(d+1) {};

Page 295: C++ Training Datascope Lawrence D’Antonio

This is illegal.

Class D is responsible for constructing its virtual base class A.

Since A has no default constructor, D’s constructor fails to construct the A subobject.

Page 296: C++ Training Datascope Lawrence D’Antonio

What’s the output?

class A { private: int x;public: A(int a): x(a) {} void print() const { std::cout << “A::x = “ << x << ‘\n’; }};

class B: public virtual A {public: B(int b): A(b+1) {}};

Page 297: C++ Training Datascope Lawrence D’Antonio

Example Part 2

class C: public virtual A {public: C(int c): A(c+2) {}};

class D: public B, public C {public: D(int d): A(d), B(d-1), C(d+1) {}};

Page 298: C++ Training Datascope Lawrence D’Antonio

Example Part 3

main() {

D d(3);

d.print();

return 0;

}

Page 299: C++ Training Datascope Lawrence D’Antonio

The output is:

A::x = 3

This is because the calls to the A constructor in B’s and C’s constructors are ignored.

Page 300: C++ Training Datascope Lawrence D’Antonio

Is this legal?

class A { private: int x;public: A(int a): x(a) {} void print() const { std::cout << “A::x = “ << x << ‘\n’; } void set_x(int a) { x = a; }};

Page 301: C++ Training Datascope Lawrence D’Antonio

Example Part 2

class B: public virtual A {

public:

B(int b): A(b+1) {}

void set_x(int a) {

A::set_x(a);

std::cout << “Hello\n”;

}

};

Page 302: C++ Training Datascope Lawrence D’Antonio

Example Part 3

class C: public virtual A {public: C(int c): A(c+2) {}};

class D: public B, public C {public: D(int d): A(d), B(d-1), C(d+1) {}};

Page 303: C++ Training Datascope Lawrence D’Antonio

Example Part 4

main() { D d(3);

d.print(); d.set_x(5); d.print();

return 0;}

Page 304: C++ Training Datascope Lawrence D’Antonio

Yes, this example is legal.

It is legal for B::set_x() to call A::set_x().

d.print() calls d.A::print()

d.set_x() calls d.B::set_x()

Page 305: C++ Training Datascope Lawrence D’Antonio

Superclass methods

A child class may specialize a method inherited from some superclass, but still need to invoke the inherited method.

In C++ one uses the scope resolution operator :: to access superclass methods.

In Smalltalk and Java, one uses the keyword super as a generic name for the superclass.

Page 306: C++ Training Datascope Lawrence D’Antonio

Superclass methods 2

Java example:

public class Superclass {

public void printMethod() {

System.out.println

("Printed in Superclass.");

}

}

Page 307: C++ Training Datascope Lawrence D’Antonio

Superclass methods 3

public class Subclass extends Superclasspublic void printMethod() { //overrides printMethod in Superclass

super.printMethod(); System.out.println("Printed in Subclass"); }

public static void main(String[] args) { Subclass s = new Subclass();

s.printMethod(); }

}

Page 308: C++ Training Datascope Lawrence D’Antonio

Superclass methods 4

Some languages, such as Dylan and CLOS, use a bottom-up method called next-method (or call-next-method).

The arguments to the method that calls next-method are supplied to next-method.

Page 309: C++ Training Datascope Lawrence D’Antonio

Superclass methods 5

The next-method operation searches the inheritance chain to find another method with the same name and parameters at a point higher in the chain.

So next-method searches for a more general version of itself.

Page 310: C++ Training Datascope Lawrence D’Antonio

Superclass methods 6

If, in the search conducted by next-method, no relevant method is found then an error results.

If a method is located then it is invoked using the same arguments as in the original method.

If two such methods are found then the more specific is called.

Page 311: C++ Training Datascope Lawrence D’Antonio

next-method example

The following example defines a class for a FIFO queue (it uses the Dylan language).

define class <FIFO>

slot elems :: LIST;

end class <FIFO>;

Page 312: C++ Training Datascope Lawrence D’Antonio

next-method example 2

Here is a method for inserting elements into the queue

define method add_element(q :: FIFO, x)

elems(q) := cons(x, elems(q))

end method;

Page 313: C++ Training Datascope Lawrence D’Antonio

next-method example 3

Now suppose we want to define a class LIFO that inherits from FIFO.

define class <LIFO> (<FIFO>)

end class <LIFO>

Page 314: C++ Training Datascope Lawrence D’Antonio

next-method example 4

We can use next-method to specialize the add_element for the LIFO class.

define method add_element(q :: FIFO, x)

elems(q) := reverse(elems(q));

next-method;

elems(q) := reverse(elems(q));

end method;

Page 315: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan

Here is a Dylan mixin class example. This defines a Windows class and related classes for drawing.

define class <window> (<object>)

slot width :: <integer>;

slot height :: <integer>;

end class <window>;

Page 316: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 2

define class <border-window> (<window>)slot border-width :: <integer>;

end class <border-window>;

define method width(window :: <border-window>)

next-method() - 2 * window.border-width; end method width;

define method height(window :: <border-window>)

next-method() - 2 * window.border-width; end method height;

Page 317: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 3

define class <label-window> (<window>) slot label-height :: <integer>; slot label-text :: <string>;

end class <label-window>;

define method height(window :: <label-window>)

next-method() - window.label-height; end method height;

Page 318: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 4

The following class uses multiple inheritance to create a Window class with features of all previously defined classes.

define class <border-label-window> (<border-window>, <label-window>,

<window>)

end class <border-label-window>;

Page 319: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 5

Here is the same example in C++

class Window { private:

int _width; int _height; public:

virtual int width() { return _width; } virtual int height() { return _height; }

};

Page 320: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 6

class BorderWindow : public virtual Windowclass BorderWindow : public virtual Window{ { private: private:

int _border_width; int _border_width;

public: public: virtual int border_width() virtual int border_width() { return _border_width; } { return _border_width; }

virtual int width(); virtual int width(); virtual int height(); virtual int height();

}; };

Page 321: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 7

int BorderWindow::width() {int BorderWindow::width() { return return

Window::width() - 2*border_width();Window::width() - 2*border_width();} }

int BorderWindow::height() int BorderWindow::height() { { return return

Window::height() - 2*border_width(); Window::height() - 2*border_width(); }}

Page 322: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 8

class LabelWindow : public virtual Window { private:

int _label_height; char *_label_text;

public: virtual int label_height() { return _label_height; }

virtual char* label_text() { return _label_text; } virtual int height();

};

Page 323: C++ Training Datascope Lawrence D’Antonio

C++ vs. Dylan 9int LabelWindow::height() { return Window::height() - label_height(); }

class BorderLabelWindow : public virtual BorderWindow, public virtual LabelWindow, public virtual Window

{ public:

virtual int height(); };

int BorderLabelWindow::height() {

return Window::height() - 2*border_width() – label_height();

}


Recommended