Engineering classes
This presentation is the property of its rightful owner.
Sponsored Links
1 / 60

Engineering Classes PowerPoint PPT Presentation


  • 48 Views
  • Uploaded on
  • Presentation posted in: General

Engineering Classes. Objectives. At the conclusion of this lesson, students should be able to: Explain why it is important to correctly manage dynamically allocated storage. Write programs that correctly use * Destructors to return dynamically allocated storage

Download Presentation

Engineering Classes

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Engineering classes

Engineering Classes


Objectives

Objectives

At the conclusion of this lesson, students should be able to:

Explain why it is important to correctly manage dynamically

allocated storage.

Write programs that correctly use

* Destructors to return dynamically allocated storage

to the system.

* Overloaded assignment operators to make a deep

copy when necessary and return dynamically allocated

storage to the system.

* Copy constructors to make a deep copy when necessary.


Memory management

Memory Management

One of the major program design issues in C++

is memory management. The mishandling of

dynamically allocated storage in C++ is among

the most serious programming errors made when

using the language.

Many of these issues can be addressed by

designing classes as Concrete Data Types.


Concrete data types

Concrete Data Types

One of the goals of good software development in C++ is to

construct each class so that it appears, to the applications

programmer, to be equivalent to a built-in type for the language.

That is, it is well behaved in all of the ways that a standard

built-in data type is well behaved.

A C++ class written in this way has been termed a “concrete

data type''. Although in detail, the implementation of a class is

specific to the class, all concrete data types have a similar

structure. Some author’s refer to this structure as the

orthodox canonical class form.


Review

local variables

size and amount

known at compile

time

global and static

variables

Review

C++ Programs can allocate objects in one of three

memory areas.

The run-time stack

The static data area or data segment

The heap or free store

storage allocated at run-time

because we don’t know how much

or what type of data will be stored

when the program is compiled.


Engineering classes

An object is allocated on the heap using

the new operator.

The allocated object has no name, but is referenced

through a pointer returned by the new operator.

Storage allocated using new must be recycled back

to the heap when the storage is no longer required.

Storage that is no longer accessible, but has not been

returned to the heap is called a memory leak.


Engineering classes

Un-initialized pointers should be set to NULL.

Pointers should also be set to NULL after

calling delete to return storage to the heap.


Destructors

Destructors

All Concrete Data Types must have a destructor if it

manages resources through a pointer.

When program execution reaches the end of a block

in which an object was declared, the storage allocated

for that object on the stack is relinquished.

If a destructor is defined for the class to which the

object belongs, the destructor is called first.

The purpose of the destructor is to clean up any

resources that the object may have acquired. The

most common resource that needs to be managed is

storage allocated from the heap.


Linked lists

Linked Lists

The concepts discussed in this slide set will be illustrated

using a linked list. Before going through the examples,

it will be necessary that you understand what a linked list

is and how they are used. This should be a review of the

material presented several weeks ago.


Memory issues

Memory Issues

node

node

node

3

12

7

list

9

null

This diagram illustrates an example of

a linked list. In this example, each node of the

list is dynamically allocated from the heap and

contains an integer value and a pointer to the

next node in the list.


Engineering classes

node

node

node

3

12

7

list

9

null

class List

{

private:

Node* head;

int length;

public:

. . .

};

the List class just contains

a pointer to the first node

in the list,

and an integer

containing the number of

elements in the list.


Engineering classes

node

node

node

3

12

7

list

9

null

class Node

{

private:

int data;

Node* next;

public:

Node* getNext( );

};

Each node object contains an integer data member and a pointer to the next node. The storage for each node is allocated from the heap as it is needed.


Engineering classes

3

list

node

node

node

12

7

9

null

So … what happens in this case when the list

object goes out of scope?

With no destructor, the pointer data member

in the list object is relinquished when the object

goes out of scope. Without this pointer, the first

node, and all subsequent nodes, become inaccessible

to the program. Since the storage for these nodes is

still owned by the program, we have a memory leak.

some block

{

List myList;

. . .

blah … blah … blah

. . .

}


Engineering classes

3

list

node

node

node

12

7

9

null

Can you come up with a destructor that keeps

The memory leak from happening?


Engineering classes

3

list

node

node

node

head

length

12

7

9

null

data

next

List::~List

{

}


Engineering classes

3

list

The following destructor will solve

the problem.

node

node

node

head

length

12

7

9

null

data

next

List::~List( )

{

Node* p = head;

while ( p!=NULL)

{

Node* pnext = p->getNext( );

delete p;

p = pnext;

}

}

this function returns the

value of next


Engineering classes

p

pnext

3

list

node

node

node

head

length

12

7

9

null

data

next

List::~List( )

{

Node* p = head;

while ( p!=NULL)

{

Node* pnext = p->getNext( );

delete p;

p = pnext;

}

}

this function returns the

value of next


Engineering classes

p

pnext

node

3

12

list

data

next

node

node

head

length

7

9

null

List::~List( )

{

Node* p = head;

while ( p!=NULL)

{

Node* pnext = p->getNext( );

delete p;

p = pnext;

}

}

this function returns the

value of next


Engineering classes

pnext

3

list

p

node

node

head

length

7

9

null

List::~List( )

{

Node* p = head;

while ( p!=NULL)

{

Node* pnext = p->getNext( );

delete p;

p = pnext;

}

}

this function returns the

value of next


Assignment operator

Assignment Operator

We just illustrated how to manage the memory

allocated dynamically for Node objects when the

list goes out of scope. However, the automatic

invocation of the destructor when the object goes

out of scope introduces another serious problem.

Consider the following …


Engineering classes

node

node

node

list_a

3

12

7

list

9

null

node

node

list_b

2

21

6

list

null


Engineering classes

list_a = list_b;

Problem: The pointer to this

Data has been lost. Memory Leak!

node

node

node

list_a

3

12

7

list

9

null

node

node

list_b

2

2

21

6

list

null

the default assignment operator does a member-by

member copy of each data member in the list objects.


Engineering classes

Now … suppose list_b goes out of scope.

node

node

node

list_a

2

12

7

list

9

null

node

node

Problem: the pointer

in list_a points to memory

no longer owned by the

program.

list_b

2

21

6

list

null

Our destructor, as specified, cleans up

the list, returning the storage for each

node to the heap.


Engineering classes

Adding insult to injury, what happens

when list_a goes out of scope?

node

node

node

list_a

2

12

7

list

9

null

this storage gets

returned twice! This

could totally destroy

the memory manager!

Storage belonging to the heap.


Engineering classes

We solve this problem by overloading the

assignment operator in the List class.

The assignment operator must do two things:

list_a = list_b;

Free the storage used by the left operand (list_a)

Make a copy the entire data structure of the right

operand (list_b) and point to it in the

left operand -– do a deep copy.


Engineering classes

Free this storage

node

node

node

list_a

3

12

7

list

9

null

node

node

list_b

2

21

6

list

null


Engineering classes

node

node

21

6

null

list_a

2

3

list

node

node

list_b

2

21

6

list

null

Make a copy the entire list …


Engineering classes

it is customary to name the parameter ‘b’

return a List reference

to allow multiple

assignment

always pass the operand

as a constant reference

const List& List::operator=(const List& b)

{

if (this ==&b) return *this;

first, check to make sure

that we are not doing the

assignment a = a; We don’t

want to clean up storage for

a if this is the case.


Engineering classes

const List& List::operator=(const List& b)

{

if (this ==&b) return *this;

Node* p = head;

while (p != NULL)

{

Node* pnext = p->getNext( );

delete p;

p = pnext;

}

this code cleans up

the storage allocated

to the left hand list.

Note: It’s the same

code we wrote for the

destructor.


Engineering classes

Next, we are going to do the copy. We are

going to do what is called a deep copy. That

is, we are going to create a complete copy of

the data structure that is on the right hand side.

A shallow copy only copies pointers, not what

they point to.


Engineering classes

start by copying the

length data.

set the Node* p to Null,

we will use this later.

length = b.length;

p = NULL;

Node* q = b.getHead( );

while (q != NULL)

{

Node* n = new Node;

n->setNext (NULL);

n->setData (q->getData( ));

if (p == NULL)

head = n;

else

p->setNext (n);

p = n;

q = q->getNext ( );

}

return *this;

then declare another

Node* q, and set it to the

head data member in list b.


Engineering classes

length = b.length;

P = NULL;

Node *q = b.getHead( );

q

list_a

n

p

NULL

list

node

node

list_b

2

2

21

6

list


Engineering classes

length = b.length;

p = NULL;

Node* q = b.getHead( );

while (q != NULL)

{

Node* n = new Node;

n->setNext (NULL);

n->setData (q->getData( ));

if (p == NULL)

head = n;

else

p->setNext (n);

p = n;

q = q->getNext ( );

}

return *this;

Now, allocate storage for

the first node in the new

list.

set its pointer to the

next node to NULL.

get the data member of the

current node in the right-hand

list and store its value in this

new node.


Engineering classes

Node *n = new Node;

n points to the new

node just created

q

NULL

list_a

n

2

p

NULL

list

q points to the current node

in the right hand list

node

node

list_b

2

21

6

list

n -> setNext (NULL);

n -> setData (q.getData( ));


Engineering classes

length = b.length;

p = NULL;

Node* q = b.getHead( );

while (q != NULL)

{

Node* n = new Node;

n->setNext (NULL);

n->setData (q.getData( ));

if (p == NULL)

head = n;

else

p->setNext (n);

p = n;

q = q->getNext ( );

}

return *this;

If this is the first node

store its pointer in the

list object.


Engineering classes

if (p == NULL)

head = n;

q

head

NULL

21

list_a

n

2

p

NULL

list

node

node

list_b

2

21

6

list


Engineering classes

length = b.length;

p = NULL;

Node* q = b.getHead( );

while (q != NULL)

{

Node* n = new Node;

n->setNext (NULL);

n->setData (q.getData( ));

if (p == NULL)

head = n;

else

p->setNext (n);

p = n;

q = q->getNext ( );

}

return *this;

set to point to the end

node in left hand list,

the one we just created

set to point to the next

node in the right hand list.


Engineering classes

p = n;

q = q->getNext( );

q

head

NULL

21

list_a

n

2

p

list

node

node

list_b

21

2

6

list


Engineering classes

We have copied the List Object and the

first Node. Since q is not null (it points to

a node) go through the while block again.


Engineering classes

list_a = list_b;

q

head

NULL

list_a

21

n

2

NULL

p

list

node

node

list_b

21

2

6

list

Node* n = new Node;

n->setNext (NULL);

n->setData (q.getData( ));


Engineering classes

list_a = list_b;

q

head

list_a

21

n

2

NULL

6

p

list

node

node

NULL

list_b

21

2

6

list

else

p->setNext (n);

p is not null, so …

p = n;

q = q->getNext ( );


Engineering classes

We have successfully copied the second

node from the right-hand list. q is now

= NULL, so we drop out of the loop.


Copy constructor

Copy Constructor

We have fixed the assignment operator so that is

correctly creates a copy of the object. However,

objects also get copied when passed by value.

When a function is called, all of the function

arguments are copied into local variables

associated with the function. When the function

exits, these variables go out of scope and are

destroyed. This will cause similar problems to the

ones we just discussed with the default assignment

operator.


Engineering classes

node

node

node

3

12

7

list_a

9

Consider the list shown. What happens in the

function invocation

double average (List a);


Engineering classes

3

node

node

node

3

12

7

list_a

9

when the function is called, a copy

of the list object goes on the stack.

The default is a shallow copy ….

stack


Engineering classes

3

node

node

node

Oh-oh!

3

12

7

list_a

9

when the function exits, all of the

variables associated with the function

go out of scope. This includes the

copy of the list object passed on the

stack. When it goes out of scope, its

destructor is called …

stack


Engineering classes

The Copy Constructor

the compiler invokes

this code automatically

whenever it needs to create

a copy of the object.

List::List(const List& b)

{

length = b.length;

Node* p =NULL;

Node* q = b.head;

while (q != NULL)

{

Node* n = new Node;

n->getNext (NULL);

n->setData (q->getData( ));

if (p == NULL)

head = n;

else

p->setNext (n);

p = n;

q = q.getNext( );

}

}

this is a copy constructor

because it takes an object

of its own type as a parameter.

If this looks familiar,

it is because it is the same

code we used to copy the

object in the overloaded

assignment operator.


Copy constructor1

Copy Constructor

It is important to note that like a normal constructor,

the function of the copy constructor is to initialize

the data members of the object that just got created.

The compiler generates the code to create the object

when you do a pass by value.


Factoring common code

Factoring Common Code

There is a lot of common code between the

destructor, the assignment operator, and the

copy constructor. We can factor this common

free and copy code out. Then the destructor,

copy constructor, and assignment operator

look as shown in the following slide.


Engineering classes

List::List (const List& b)

{

copy(b);

}

List::~List( )

{

free( );

}

const List& List::operator=(const List& b)

{

if (this != &b)

{

free( );

copy(b);

}

return *this;

}


The run time stack

The Run-Time Stack

A stack is a first-in

last-out data structure.


The run time stack1

The Run-Time Stack

Data comes off of the stack

in the opposite order of how it

was out on.


The run time stack2

Local

variables

Local

variables

return

address

return

address

Stack frame

for a( )

Stack frame

for b( )

parameters

parameters

The Run-Time Stack

When a function is invoked,

space is allocated on the stack for:

the function’s parameters

the function’s return address

the function’s locally declared variables

This is called an Activation Record

or Stack Frame


Reference counts

Reference Counts

Suppose that two objects share a common value:

value

How would you manage this

if the value were dynamically

allocated?


Reference counts1

Reference Counts

Suppose that two objects share a common value:

Reference Count

value

2

How would you manage this

if the value were dynamically

allocated?


Reference counts2

Reference Counts

What would the destructor do in this case?

Reference Count

value

2

1


The bridge pattern

The Bridge Pattern

Also known as the Handle/Body pattern, the bridge

pattern provides a way for the programmer to

completely hide the details of an implementation.


Engineering classes

C++ provides encapsulation and data abstraction by

using the private keyword to hide the details of a

class’s implementation.

However, the private keyword only prevents access to

the class’s private elements, it does not really hide them.

A programmer only need look at the class’s header file

to glean a great deal of information about how a class

is implemented.

What if you really want to hide the details? For example,

what if you have a proprietary algorithm you don’t want

users of your class to know about.


Example

Example

The Impl class contains the actual

implementation details. The pointer

_theImpl is used to pass a request on

to an object of the Impl class.

For example, in the Handle class, the

foo( ) function might be written as

void Handle::foo ( )

{

_theImpl -> foo( );

}

class Handle

{

public:

Handle ( );

~Handle( );

void foo ( );

private:

Impl* _theImpl;

};


Engineering classes

A good paper on the Bridge design pattern

can be found here:


  • Login