270 likes | 369 Views
Synthetic OOD concepts and reuse Lecture 4: Separation of concerns. Topics: Complex concern: Memory management Example: Complex operations on composite structures Problem: Memory leaks Solution: Reference counting. Motivation. Suppose we define a function: Expr* mkExpr(void) {
E N D
Synthetic OOD concepts and reuseLecture 4: Separation of concerns Topics: Complex concern: Memory management Example: Complex operations on composite structures Problem: Memory leaks Solution: Reference counting CSE 335: Software Design
Motivation Suppose we define a function: Expr* mkExpr(void) { Expr* l1 = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l1,v); Expr* l2 = new Literal(2); Expr* t2 = new Multiply(l2,t1); } CSE 335: Software Design
Problem Suppose a client program executes the code: ... Expr* e = mkExpr(); e->print(cout); delete e; ... What happens? How could we ensure that when a composite object is deleted, its parts are also deleted? CSE 335: Software Design
Solution class Expr { ... virtual ~Expr() {} }; class BinaryExpr : public Expr { ... ~BinaryExpr() { delete(leftOperand); delete(rightOperand); } ... }; CSE 335: Software Design
Or is it? Often desirable for two or more composite objects to share the same component Example: Expr* l = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l,v); Expr* t2 = new Multiply(l,t1); Here, object l is the child of two composite objects, t1 and t2. Will our memory-reclamation scheme work now? CSE 335: Software Design
Deleting composites that share parts Not safe for destructor of a composite class to delete its children if these objects might be children of multiple composite objects Solution: design a protocol that tracks references to objects and that deletes an object only when its reference count reaches 0. • Clients never call delete • Objects call delete on themselves! • Clients must explicitly notify objects when references to these objects are assigned or overwritten CSE 335: Software Design
Abstract ref-counting class class RCObject { public: void addRef() { ++refCount; } void removeRef() { if(--refCount <= 0) delete this; } protected: RCObject() : refCount(0) {} virtual ~RCObject() {} private: int refCount; }; CSE 335: Software Design
Example Modify class Expr as follows: class Expr : public RCObject { ... }; Subsequently, every class that derives from Expr will inherit reference counting infra-structure CSE 335: Software Design
Example Modify BinaryExpr as follows: class BinaryExpr : public Expr { ... BinaryExpr( Expr* l, Expr* r ) : leftOperand(l), rightOperand(r) { l->addRef(); r->addRef(); } ... ~BinaryExpr() { l->removeRef(); r->removeRef(); } }; CSE 335: Software Design
Example (continued) Now we write a function that creates an expression: Expr* createExpression(void) { Expr* l = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l,v); Expr* t2 = new Multiply(l,t1); return t2; } Requires client to increment refCount of root object (pointed to here by t2 before it goes out of scope) CSE 335: Software Design
Expr* createExpression(void) { Expr* l = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l,v); Expr* t2 = new Multiply(l,t1); return t2; } Step 1 l : Literal refCount=0 CSE 335: Software Design
Expr* createExpression(void) { Expr* l = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l,v); Expr* t2 = new Multiply(l,t1); return t2; } Step 2 l : Literal refCount=0 v : Variable refCount=0 CSE 335: Software Design
Expr* createExpression(void) { Expr* l = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l,v); Expr* t2 = new Multiply(l,t1); return t2; } Step 3 t1 : Add refCount=0 l : Literal refCount=1 v : Variable refCount=1 CSE 335: Software Design
Expr* createExpression(void) { Expr* l = new Literal(2); Expr* v = new Variable(“x”); Expr* t1 = new Add(l,v); Expr* t2 = new Multiply(l,t1); return t2; } Step 4 t2 : Multiply refCount=0 t1 : Add refCount=1 l : Literal refCount=2 v : Variable refCount=1 CSE 335: Software Design
Example (continued) Client code then uses as follows: ... Expr* e = createExpression(); e->addRef(); // new line e->print(cout); e->removeRef(); // rather than delete(e) ... CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Adding reference to returned object e t2 : Multiply refCount=1 t1 : Add refCount=1 l : Literal refCount=2 v : Variable refCount=1 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Printing expression... e t2 : Multiply refCount=1 t1 : Add refCount=1 l : Literal refCount=2 v : Variable refCount=1 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 1 e t2 : Multiply refCount=0 t1 : Add refCount=1 l : Literal refCount=1 v : Variable refCount=1 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 2 e t2 : Multiply refCount=0 t1 : Add refCount=0 l : Literal refCount=1 v : Variable refCount=1 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 3 e t2 : Multiply refCount=0 t1 : Add refCount=0 l : Literal refCount=0 v : Variable refCount=1 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 4 e t2 : Multiply refCount=0 t1 : Add refCount=0 l : Literal refCount=0 v : Variable refCount=1 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 5 e t2 : Multiply refCount=0 t1 : Add refCount=0 v : Variable refCount=0 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 6 e t2 : Multiply refCount=0 t1 : Add refCount=0 v : Variable refCount=0 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 7 e t2 : Multiply refCount=0 t1 : Add refCount=0 CSE 335: Software Design
e->addRef(); e->print(cout); e->removeRef(); Removing ref: Step 8 e t2 : Multiply refCount=0 CSE 335: Software Design
Conceptual (but flawed) alternative implementation of createExpression() Expr* createExpression(void) { Expr* l = new Literal(2); l->addRef(); Expr* v = new Variable(“x”); v->addRef(); Expr* t1 = new Add(l,v); t1->addRef(); Expr* t2 = new Multiply(l,t1); t2->addRef(); l->removeRef(); v->removeRef(); t1->removeRef(); t2->removeRef(); // What happens here??? return t2; } CSE 335: Software Design
“Mixin” classes RCObject is an example of a “mixin” class • So-called because designed to encapsulate orthogonal functionality that may be “mixed into” classes in an existing hierarchy • Mixin classes are always abstract • May use multiple inheritance to incorporate functionality in a mixin class if base class does not need it or if base class needs functionality from multiple mixin classes CSE 335: Software Design