k. stirewalt cse 335: software design foundations: language mechanisms and primitive oo concepts...

Post on 21-Dec-2015

220 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

CSE 335: Software Design K. Stirewalt

Foundations: Language Mechanisms and Primitive OO ConceptsLecture 2: Polymorphism

Topics:– Polymorphism and virtual functions in C++– Simple uses of polymorphism– Implementation of virtual and non-virtual functions– Common mistakes and need for virtual destructors

CSE 335: Software Design K. Stirewalt

Pop quiz

void generateReport( Employee& empl ){ ... empl.print(cout); ...}

int main(void){ Employee doe(“John”, “Doe”, 235); Manager howell(“Charles”, “Howell”, 235, 3);

generateReport(howell);}

Question: Which method invoked to carry out this operation when empl is actually an instance of Manager?

CSE 335: Software Design K. Stirewalt

Motivation

We should be able to develop clients that request an object to perform some operation and leave it to the object to choose the method

This capability, called polymorphism, is one of the defining characteristics of OO

In C++, we implement polymorphic operations by declaring member functions to be virtual

CSE 335: Software Design K. Stirewalt

Example: declaring virtual functionclass Employee { private: string first_name, last_name; short department;

public: virtual void print( ostream& ) const;};

class Manager : public Employee {

private:

list<Employee*> group;

short level;

public:

void print( ostream& ) const;

};

Observe: function member declared virtual in base class.

Observe: no need to “redeclare” print as virtual.

CSE 335: Software Design K. Stirewalt

Example: Virtual functions

int main(void)

{

Employee doe(“John”, “Doe”, 235);

Manager howell(“Charles”, “Howell”, 235, 3);

doe.print(cout); // invokes Employee::print()

howell.print(cout); // invokes Manager::print()

Employee* ePtr = &howell;

ePtr->print(cout); // invokes Manager::print()

}

CSE 335: Software Design K. Stirewalt

Virtual-function definitions

void Employee::print( ostream& os ) const

{

os << “Name:” << first_name << “ “ << last_name

<< endl << “Dept: “ << department;

}

void Manager::print( ostream& os ) const

{

Employee::print(os);

os << “Level: “ << level;

}

Question: What happens here???

CSE 335: Software Design K. Stirewalt

Exercise

We want to store different geometric objects in a list and compute their area. Develop a class hierarchy with classes Shape, Circle, and Rectangle. The following code should compile correctly:

list<Shape*> myShapes;

Circle* circ = new Circle(10);

Rectangle* rect = new Rectangle(20, 40);

myShapes.push_back(circ);

myShapes.push_back(rect);

for (list<Shape*>::iterator it = myShapes.begin();

it != myShapes.end(); it++)

cout << (*it)->area() << endl;

CSE 335: Software Design K. Stirewalt

Heterogeneous containers

Inheritance + polymorphism can be used to implement containers that can hold different types of elements– Declare base class from which all element classes derive

• Base class declares a polymorphic operation– In C++, developer of base class declares a virtual member function

• Each element class may provide its own method for the operation

– Instantiate container with type “pointer to base class”– Clients, invoke operations on elements taken from

container without knowing actual type of the elements!

CSE 335: Software Design K. Stirewalt

<<DETOUR>>

Henceforth, we will use polymorphism frequently and in combination with other language features to build reliable and reusable software structures

Because it is so central, it is important that you REALLY understand how it works

My theory: No better way than to look at how it is implemented!

CSE 335: Software Design K. Stirewalt

Question

Thinking in terms of system-level resources, what is the mechanism by which virtual functions are actually implemented?

Answer: Each instance of a class that declares or inherits virtual member functions carries with it a pointer to a table (called a vtable) of pointers to member functions– When a virtual member function is invoked, a pointer to the function

to be dispatched is found by looking it up in the vtable.– Instances of different classes point to different vtables.– Owing to this indirection, a client may invoke the method

appropriate to the class used to originally instantiate the object without knowing that class!

CSE 335: Software Design K. Stirewalt

Recall: Conceptual model of memory use by a running process

Process partitioned into 2 major chunks:– static part

• contains program code +• ... static data, vtables, etc

– dynamic part• contains run-time stack +

heap• all local variables and objects

created using new live here

Run-timestack

Heap

Programcode +

static data

CSE 335: Software Design K. Stirewalt

Consider the following source codeclass C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

Note: No virtual member functions yet; we’ll bring those in later.

CSE 335: Software Design K. Stirewalt

Code compiled and linked...class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

main() {…}

a.outCompilation/linking

CSE 335: Software Design K. Stirewalt

Program is executed...

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

main() {…}

CSE 335: Software Design K. Stirewalt

Program is executed...

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

main() {…}

Process begins executing in function main

CSE 335: Software Design K. Stirewalt

Object myC1 constructed...

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

main() {…}

832.0

myC1attr1attr2

CSE 335: Software Design K. Stirewalt

Object myC2 constructed

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

main() {…}

832.0

myC1

myC216-64.5

attr1attr2attr1attr2

CSE 335: Software Design K. Stirewalt

-64.516

vf2() invoked on myC2

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … void vf1(…){…} void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

main() {…}

832.0

myC1

myC2

attr1attr2attr1attr2

C::vf2activation

this

CSE 335: Software Design K. Stirewalt

Now let’s bring in the virtuals!

CSE 335: Software Design K. Stirewalt

Consider these modifications to our source code

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

Note: Only change to source is introduction of the 2 virtual keywords

CSE 335: Software Design K. Stirewalt

main() {…}

Compiling and linking…

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

Class Cvtable

a.out

Note: Existence of virtual functions in class C caused the compiler to create a static object called the class C vtable.

CSE 335: Software Design K. Stirewalt

main() {…}

Program is executed

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

Class Cvtable

CSE 335: Software Design K. Stirewalt

main() {…}

Object myC1 created...

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

Class Cvtable

attr1attr2

vptr

CSE 335: Software Design K. Stirewalt

main() {…}

Object myC1 created...

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

Class Cvtable

attr1attr2

vptr

Note: “extra” field in myC1

CSE 335: Software Design K. Stirewalt

main() {…}

Creating myC2

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

myC216-64.5

Class Cvtable

attr1attr2

vptr

attr1attr2

vptr

CSE 335: Software Design K. Stirewalt

main() {…}

Creating myC2

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

myC216-64.5

Class Cvtable

attr1attr2

vptr

attr1attr2

vptr

Note: “extra” field in myC2

CSE 335: Software Design K. Stirewalt

main() {…}

Dynamic dispatch of vf2 [step 1]

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

myC216-64.5

Class Cvtable

attr1attr2

vptr

attr1attr2

vptr

CSE 335: Software Design K. Stirewalt

main() {…}

Dynamic dispatch of vf2 [step 2]

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

myC216-64.5

Class Cvtable

attr1attr2

vptr

attr1attr2

vptr

CSE 335: Software Design K. Stirewalt

main() {…}

Dynamic dispatch of vf2 [step 3]

class C { protected: unsigned attr1; float attr2; …

public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…}};

int main(void){ C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2();}

C::f1(…){…}

…C::vf1(…){…}

C::vf2(…){…}…

832.0

myC1

myC216-64.5

Class Cvtable

attr1attr2

vptr

attr1attr2

vptr

C::vf2activation

...

CSE 335: Software Design K. Stirewalt

More interesting example

class D : public C { protected: unsigned attr3; …

public: void f1(…); … void vf1(…); void vf2(…); virtual void vf3(…);};

int main(void){ C myC1(8,32.0); D myD1(16, -64.5, 38); myD1.vf2()}

CSE 335: Software Design K. Stirewalt

More interesting example

class D : public C { protected: unsigned attr3; …

public: void f1(…); … void vf1(…); void vf2(…); virtual void vf3(…);};

int main(void){ C myC1(8,32.0); D myD1(16, -64.5, 38); myD1.vf2()}

Question: How would the process memory map look in this example?

CSE 335: Software Design K. Stirewalt

More interesting example

C::f1(…)

class D : public C { protected: unsigned attr3; …

public: void f1(…); … void vf1(…); void vf2(…); virtual void vf3(…);};

int main(void){ C myC1(8,32.0); D myD1(16, -64.5, 38); myD1.vf2();}

D::vf2(…)C::vf1(…)

C::vf2(…)

Class C vtable

832.0

myC1

16-64.5

myD1

D::vf3(…)

vptr

attr1attr2

vptr

attr1attr2

38attr3

Class D vtable

D::vf1(…)D::f1(…)

main(…)

CSE 335: Software Design K. Stirewalt

Exercise

void myPrint( Employee e )

{

e.print(cout);

}

int main(void)

{

Manager howell( … );

myPrint(howell);

}

Question: Which print method invoked when actual parameter is Manager?

Observe: Actual parameter is a value, not a reference.

CSE 335: Software Design K. Stirewalt

Question

A compiler translates a program in one language into an equivalent program in another language, which is “closer to the machine”.

How would the compiler-generated code for function myPrint differ when Employee::print is virtual vs. non-virtual?

CSE 335: Software Design K. Stirewalt

Code for myPrint (print NOT virtual)

pushl %ebpmovl %esp, %ebp

movl 8(%ebp), %eaxpushl $_coutpushl %eax

call _print__8EmployeeR7ostream

movl %ebp, %esppopl %ebp

ret

CSE 335: Software Design K. Stirewalt

Code for myPrint (print virtual)

pushl %ebpmovl %esp, %ebp

movl 8(%ebp), %edxpushl $_coutpushl %edx

movl 4(%edx), %ecx ; put vptr in ecxmovl 12(%ecx), %eax ; put address at offset

; 12 in vtable into eaxcall *%eax ; call function pointed to

; by eax

movl %ebp, %esppopl %ebp

ret

CSE 335: Software Design K. Stirewalt

Observations

Lots of things happen when we define an member function to be virtual– Better left to the compiler– No significant inefficiency (one extra indirection

per virtual function invocation)

CSE 335: Software Design K. Stirewalt

Destructors in derived classes

class Target { public: Target() { numtargets++; } ~Target() { --numtargets; }

static int numberOfTargets() { return numtargets; }

private: static int numtargets;};

class Tank : public Target { public: Tank( const string& tId ) : tankId(tId) { numtanks++; }

~Tank() { numtanks--; }

static int numberOfTanks() { return numtanks; }

private: static int numtanks; const string tankID;};

CSE 335: Software Design K. Stirewalt

Exercise

Target* target = new Tank(“T-19”);...delete target;

What happens here? (Think about what destructors are called)

Notice: base and derived classes

CSE 335: Software Design K. Stirewalt

Virtual Destructor

class Target { public: Target() { numtargets++; } virtual ~Target () { --numtargets; }

static int numberOfTargets() { return numtargets; }

private: static int numtargets;};

Solution: Makedestructor virtual.

top related