• No results found

8. Run-Time Arrays — Intro. to Pointers

N/A
N/A
Protected

Academic year: 2021

Share "8. Run-Time Arrays — Intro. to Pointers"

Copied!
54
0
0

Loading.... (view fulltext now)

Full text

(1)

1

8. Run-Time Arrays — Intro. to Pointers

For declarations like double doubleVar;

char charVar = 'A';

int intVar = 1234;

the compiler constructs the object being declared, which means that it:

1. Allocates memory needed for that type

of object

2. Associates the object's name with that

memory

3. Initializes that memory

Read (§8.4 & §8.5)

(2)

2

Addresses

& is the address operator in C++

&variable is the address (actual memory location) of variable Example: For the previous scenario:

Values of &intVar, &charVar, and &doubleVal

  

0x1220, 0x1224, and 0x1225 To make addresses more useful, C++ provides pointer variables.

Definition: A pointer variable (or simply pointer) is a variable whose

value is a memory address.

Declarations: Type * pointerVariable

declares a variable named pointerVariable

that can store the address of an object of type Type.

(3)

3

#include <iostream>

using namespace std;

int main()

{ int i = 11, j = 22;

double d = 3.3, e = 4.4;

// pointer variables that:

// store addresses of ints

Output produced:

&i = 0x7fffb7f4

&j = 0x7fffb7f0

&d = 0x7fffb7e8

&e = 0x7fffb7e0

int * iptr, * jptr;

double * dptr, * eptr;

iptr = &i;

jptr = &j; // value of iptr is address of i // value of jptr is address of j dptr = &d; // value of dptr is address of d eptr = &e; // value of eptr is address of e cout << "&i = " << iptr << endl

<< "&j = " << jptr << endl << "&d = " << dptr << endl << "&e = " << eptr << endl;

}

// store addresses of doubles

(4)

4

Dereferencing

* is the dereferencing (or indirection) operator

It can be used to access the value stored in a location.

For an expression of the form*pointervariable

the value produced is not the address stored in pointerVariable but is instead

the value stored in memory at that address.

Value of dptr: 0x7fffb7e8 Value of *dptr: 3.3

We say dptr points to that memory location (whose address is

0x7fffb7e8)

3.3

*dptr

(5)

5

Suppose we replace the preceding output statements by:

cout << "i = " << *iptr << endl << "j = " << *jptr << endl << "d = " << *dptr << endl << "e = " << *eptr << endl;

Output produced:

i = 11 j = 22 d = 3.3 e = 4.4

(*iptr)++;

cout << "i = " << i << endl;

i = 12

(6)

6

A Note About Reference Parameters:

Recall the C++ function to exchange the values of two int variables:

void Swap(int & a, int & b)

{ int temp = a; a = b; b = temp; }

The values of two int variables x and y can be exchanged with the call:

Swap(x,y);

The first C++ compilers were just preprocessors that read a C++

program, produced functionally equivalent C code, and ran it through the C compiler. But C has no reference parameters.

How were they handled?

Translate the function to

void Swap(int * a, int * b)

{ int temp = *a; *a = *b; *b = temp; }

and the preceding call to: Swap(&x, &y);

This indicates how the call-by-reference parameter mechanism works:

A reference parameter is a variable containing the

address of its argument (i.e., a pointer variable)

and that is automatically dereferenced when used.

(7)

7

Anonymous variables

Definitions:

A variable is a memory location.

A named variable has a name associated with its memory location, so that this memory location can be accessed conveniently.

An anonymous variable has no name associated with its memory location; if the address of that

memory location

is stored in a pointer variable, then the variable can be accessed indirectly using the pointer.

(8)

8

Named variables are created using a normal variable declaration.For example, the declaration

int j = 22;

i. constructed an integer (4-byte) variable at memory address 0x7fffb7f4 and initialized those 4 bytes to the value 22; and

ii. associated the name j with that address, so that all subsequent uses of j refer to address 0x7fffb7f4;

the

statement

cout << j << endl;

will display the 4-byte value (22) at address 0x7fffb7f4.

(9)

9

new operator

Anonymous variables are created using the new operator, whose form is:

new Type

When executed, this expression:

i. allocates a block of memory big enough for object of type Type,

and

ii. returns the starting address of that block of memory.

Type * ptr; ptr

ptr = new Type; ptr Memory

for Type value

?

(10)

10

Example:

#include <iostream>

using namespace std;

int main() {

double * dptr, * eptr;

dptr = new double;

eptr = new double;

cout << "Enter two numbers: ";

cin >> *dptr >> *eptr;

cout << *dptr << " + " << *eptr

<< " = " << *dptr + *eptr << endl;

}

Sample run:

Enter two numbers: 2.2 3.3 2.2 + 3.3 = 5.5

(11)

11

The program uses the new operator to allocate two anonymous variables whose addresses are stored in pointer variables dptr and eptr:

Note 1: We could have performed these allocations as initializations in the declarations of dptr and eptr:

double * dptr = new double, * eptr = new double;

anonymous variables dptr

eptr

dptr eptr

2.2 3.3

The input values are then stored in these indirectly via the pointers dptr and eptr:

(12)

12

Note 2: new must be used each time a memory allocation is needed.

double *dptr, *eptr;

dptr = eptr = new double;

eptr = new double allocates memory for a double and assigns its address to eptr.

dptr = eptr simply assigns this same address to dptr; it does not allocate new memory.

*dptr = 3.14159;

*eptr = 0.1423;

cout << *dptr << " " << *eptr << endl;

What’s output?

0.1423 0.1423

(13)

13

Note 3: What if we changed this to:

double *dptr, *eptr;

dptr = new double;

*dptr = 3.14159;

*eptr = 0.1423;

The first assignment is okay and stores 3.14149 in the memory location pointed to by eptr.

However, the second results in the most dreaded of all errors:

An attempt to dereference an undefined pointer variable (i.e., one to which no memory address has been assigned) produces

a segmentation fault.

Note 4: It is an error to attempt to allocate the wrong type of memory block to a pointer variable; for example,

double dptr = new int; // error produces a compiler error.

(14)

14

Run-time arrays

Recall that arrays as we know them have their capacities fixed at compile time. For example, array a declared by

double a[50];

has exactly 50 elements.

This is adequate if a fixed-capacity array can be used to store all of the data sets being processed. However, this is often not true because the sizes of data sets vary. In this case, we must either:

— Make array's capacity large enough to handle biggest data set — an obvious waste of memory for smaller data sets.

or

— Change capacity in array's declaration in source code and recompil

(yuck!).

It would be nice if the user could specify the capacity of the array at

run time and an array of that capacity would then be allocated and used. This is possible in C++.

(15)

15

Allocating an Array During Run-Time:

The operator new can be used in an expression of the form

new Type[n]

where n is an integer expression, to allocate an array with n elements, each of type Type; it returns the base address of that array.

This allocation occurs when this expression is executed, that is, at

run-time, not at compile-time. This means that the user can input a capacity, and the program can allocate an array with

exactly that many elements!

n-1

(16)

16

The address returned by new must be assigned it to a pointer of type Type.

So a declaration of a run-time-allocated array is simply a pointer

declaration: Type * arrayPtr;

int numItems;

double dub[20]; // an ordinary compile-time array double *dubPtr; // a pointer to a (run-time) array

cout << "How many numbers to process? "; cin >> numItems;

dubPtr = new double[numItems];

Recall: The value of an ordinary array like dub is the base address of the array. So, in a subscript expression like

dub[i] ( same as operator[](dub, i)) the subscript operator actually takes two operands: the base address of the array and an integer index. Since pointer

variable dubPtr also is the base address of an array, it can be used in the same manner as an array name:

dubPtr[i] ( same as operator[](dubPtr, i)) Example: for (int i = 0; i < numItems; i++)

cout << dubPtr[i] << endl;

(17)

17

Memory Allo-/Deallo-cation

new receives its memory allocation from pool of available memory called the heap or free store. It is usually located between a program and its run-time stack.

Program statements Run-Time

Stack

The run-time stack grows each time a function is Heap

called, so it is possible for it to overun the heap (if main() calls a function that calls a function that calls a function ... e.g., due to infinite or too deeply nested recursion) It is also possible for the heap to overun the run-time stack due to too many (or too large) new operations.

(18)

18

When the heap has been exhausted, new returns the value 0 (called the null address or null pointer).

Common to picture a null pointer variable using the electrical engineering ground symbol:

dptr

Attempting to dereference a null (or uninitialized or void) pointer variable produces a segmentation fault.

So check whether a pointer is null before attempting to dereference it.

double *dptr = new double;…

if (dptr == 0) // check if pointer has null value {

cerr << "\n*** No more memory!\n";

exit(-1);

}

or use an assertion: (Remember to #include <cassert>)

assert(dptr != 0);

(19)

19

delete operator

Run-time stack grows each time a function is called, and shrinks again when that function terminates.

Need an analogous method to reclaim memory allocated by new, i.e.

shrink the heap when an anonymous variable is no longer needed.

Otherwise a memory leak (memory allocated but not usable) results.

For this, C++ provides the delete operation:

delete pointerVariable;

which deallocates the block of memory

whose address is stored in pointerVariable

(20)

20

For run-time arrays, to return to the heap memory allocated to array pointed to by arrayPtr. use the delete operator in the form

delete[] arrayPtr;

Important to do this because memory leaks involving arrays can result in considerable loss of memory.

For exampls:

for(;;) {

int n;

cout << "Size of array (0 to stop): ";

cin >> n;

if (n == 0) break;

double * arrayPtr = new double[n];

// process arrayPtr . . .

}

Each new allocation of memory to arrayPtr maroons old memory block

(21)

21

int *ptr1 = new int; // integer allocated

*ptr1 = 103; // anonymous integer initialized int *ptr2; // no integer allocated

ptr2 = new int(104); // anonymous integer allocated // and initialized

new and delete for Class Objects

If Type is a class, new Type will i. Allocate memory

ii. Call Type's constructor to create a value to use to initialize this memory block.

new can also be used in the form

new ClassName(initVal) to call an explicit-value constructor.

If Type is a class, delete Type will

i. Call Type's destructor to destroy the object in this memory block.

ii. Deallocate the memory

(22)

22

Run-time Allo-Deallo-cation in a Class

Classes that use run-time allocated storage require some new function members and modifications of others:

1.Destructors: To "tear down" the storage structure and deallocate its memory.

2.Copy constructors: To make copies of objects (e.g., value parameters)

3. Assignment: To assign one storage structure to another.

We will illustrate these using our Stack class

(23)

23

Data Members

We will use a run-time allocated array so that the user can specify the capacity of the stack during run time. We simply change the declaration of the

myArray member to a pointer and STACK_CAPACITY to a variable; to avoid confusion, we will use different names for the data members.

//***** RTStack.h *****

. . .

template <class StackElement>

class Stack {

/***** Function Members *****/

public:

. . .

/***** Data Members*****/

private:

StackElement * myArrayPtr; // run-time array to store elements

int myCapacity_, // capacity of stack myTop_; // top of stack

};

(24)

24

Class Constructors

Want to permit declarations such as

Stack<int> s1, s2(n);

to construct s1 as a stack with some default capacity, to construct s2 as a stack with capacity n.

To permit both forms, we use a constructor with a default argument.

/* --- Class constructor ---

Precondition: A stack has been defined.

Receive: Integer numElements > 0; (default = 128) Postcondition: stack constructed with capacity

numElements

*/

Stack(int numElements = 128);

This constructor must really construct something (and not just initialize data members):

(25)

25

template <class StackElement>

Stack<StackElement>::Stack(int numElements) {

assert (numElements > 0); // check precondition myCapacity_ = numElements; // set stack capacity // allocate array of this capacity myArrayPtr = new StackElement[myCapacity_];

if (myArrayPtr == 0) // memory available?

{

cerr << "Inadequate memory to allocate stack \n";

exit(-1);

} // or assert(myArrayPtr != 0);

myTop_ = -1;

}

Now a program can use:

cin >> num;

Stack<double> s1, s2(num);

s1 will be constructed as a stack with capacity 128 and s2 will be constructed as a stack with capacity num.

(26)

26

Other stack operations: empty, push, top, pop, output

The prototype and definition of empty() as well as the prototypes of push(),

top(), pop(), and operator<<() are the same as before (except for some name changes). See pages 428-3

Their definitions require accessing the elements of the array data member. As we have noted, the subscript operator [] can be used in the same manner for run-time allocated arrays as for ordinary arrays, and thus (except for name changes), the definitions of these functions are the same as before; for example:

//*** Definition of push() template <class StackElement>

void Stack<StackElement>::push(const StackElement & value) {

if (myTop_ < myCapacity_ - 1) {

++myTop_;

myArrayPtr[myTop_] = value;

} // or simply, myArrayPtr[++myTop_] = value;

else

cerr << "*** Stack is full -- can't add new value ***\n";

}

(27)

27

Class Destructor

For any class object obj we have used up to now, when obj is declared, the class constructor is called to initialize obj. When the lifetime of obj is over, its storage is reclaimed automatically because the location of the memory

allocated is determined at compile-time.

For objects created during run-time, however, a new problem arises. To illustrate, consider a declaration

Stack<double> st(num);

The compiler knows the data members

myCapacity_, myTop_, and myArrayPtr

of st so it can allocate memory for them: myArrayPtrmyTop_

myCapacity_

st

Array to store stack elements is created by the constructor; so memory for it isn't allocated until run-time:

myArrayPtr myTop_

myCapacity_

st

0 1 2 3 4 num-1

(28)

28

When the lifetime of st ends, the memory allocated to myCapacity_,

myTop_, and myArrayPtr is automatically reclaimed, but not for the run-time allocated array:

0 1 2 3 4 . . . num-1

Marooned!

We must add a destructor member function to the class to avoid this memory leak.

Destructor's role: Deallocate memory allocated at run-time (the opposite of the constructor's role).

At any point in a program where an object goes out of scope, the compiler inserts a call to this destructor.

That is:

When an object's lifetime is over, its destructor is called first.

(29)

29

Form of destructor:

Name is the class name preceded by a tilde (~).

It has no arguments or return type.

For our Stack class, we use the delete operation to deallocate the run-time array.

/* --- Class destructor ---

Precondition: Lifetime of Stack containing this function should end.

Postcondition: The run-time array in the Stack containing this function has been deallocated.

---*/

~Stack();

// Following class declaration // Definition of destructor template <class StackElement>

Stack<StackElement>::~Stack() {

delete[] myArrayPtr;

}

(30)

30

Suppose st is

When st's lifetime is over, st.~Stack() will be called first, which produces

Memory allocated to st — myCapacity_, myTop_, and myaArrayptr — will then be reclaimed in the usual

manner.

myArrayPtr myTop_

myCapacity_

st

0 1 2 3 4 a b c

5 2

myArrayPtr myTop_

myCapacity_

st 5

2

(31)

31

Class Copy Constructor

Is needed whenever a copy of a class object must be built, which occurs:

When a class object is passed as a value parameter

When a function returns a class object

If temporary storage of a class object is needed

In initializations

If a class has no copy constructor, the compiler uses a default copy constructor that does a

byte-by-byte copy of the object.

This has been adequate for classes without dynamically allocated data, but is not adequate for classes containing

pointers to run-time allocated arrays (or other structures) . It gives rise to the aliasing problem.

(32)

32

Example: A byte-by-byte copying of st to produce a copy stCopy gives

Not correct — copies of myCapacity_, myTop_, and myArrayPtr were made, but not a copy of the run-time allocated array.

Modifying stCopy will modify st also! Need to create a distinct copy of st, in which the array in stCopy has exactly the same elements as the array in st:

stCopy

myArrayPtr myTop_

myCapacity_

st 5

2

myArrayPtr myTop_

myCapacity_ 5 2

0 1 2 3 4 a b c

0 1 2 3 4 a b c

0 1 2 3 4 a b c

stCopy

myArrayPtr myTop_

myCapacity_

st 5

2

myArrayPtr myTop_

myCapacity_ 5 2

(33)

33

Form of copy constructor:

It is a constructor so it must be a function member, its name is the class name, and it has no return type

 It needs a single parameter (source of copy) whose type is the class;

this must be a reference parameter and should be const since it does not change this parameter or pass information back through it.

(Otherwise it would be a value parameter, and since a value

parameter is a copy of its argument, a call to the copy instructor will try and copy its argument, which calls the copy constructor, which will try and copy its argument, which calls the copy constructor . . . ) /* --- Copy Constructor ---

* Precondition: A copy of a stack is needed

* Receive: The stack to be copied (as a const * reference parameter)

* Postcondition: A copy of original has been constructed.

************************************************************/

Stack(const Stack<StackElement> & original);

(34)

34

template <class StackElement>

Stack<StackElement>::Stack(const Stack<StackElement> &

original) {

myCapacity_ = original.myCapacity_; // copy myCapacity_

member

myTop_ = original.myTop_ ; // copy myTop_ member

myArrayPtr = new StackElement[myCapacity];

// allocate array in copy if (myArrayPtr == 0) // check if memory available {

cerr << "*Inadequate memory to allocate stack ***\n";

exit(-1);

}

// copy array member

for (int pos = 0; pos < myCapacity_; pos++) myArrayPtr[pos] = original.myArrayPtr[pos];

}

(35)

35

Assignment

Aanother operation that requires special attention for classes

containing pointers to run-time arrays (or other structures). Like the copy constructor, the default assignment operation does byte-by-

byte copying. With it, the assignment statement s2Copy = s2;

will yield aliases as before; the myArrayPtr data members of both s2 and s2Copy will both point to the same anonymous array.

Need to overload the assignment operator (operator=) so that it creates a distinct copy of the stack being assigned.

operator= must be a member function. So an assignment stLeft = stRight;

will be translated by the compiler as

stLeft.operator=(stRight);

(36)

36

Prototype:

/* --- Assignment Operator ---

* Receive: Stack stRight (the right side of the assignment * operator

* Postcondition: The Stack will be a copy of stRight * Return: A reference to the current Stack

************************************************************/

Stack<StackElement> & operator=

(const Stack<StackElement> & original);

The return type is a reference to a Stack since

operator=() must return the object on the left side of the assignment and not a copy of it (to make chaining

possible).

(37)

37

Definition of operator=:

It is quite similar to that for the copy constructor, but there are some differences:

1. Object on the left side of the assignment may already have a value. Must destroy old object — deallocate the old so no

memory leak

and allocate a new one.

2. Assignment must be concerned with self-assignments: st = st;. Can't destroy the right old value in this case.

3. operator=() must return the Stack containing this function.

For #3 we use the following property of classes:

Every member function of a class has access to a (hidden) pointer constant

whose value is the address of the object containing this this function.

The expression

*this refers to the object itself.

(38)

38

template <class StackElement>

Stack<StackElement>& Stack<StackElement>::operator=

(const Stack<StackElement>& original) {

if (this != &original) // check that not st = st {

delete[] myArrayPtr; // destroy previous array myArrayPtr = new StackElement[myCapacity_];

if (myArrayPtr == 0) // check if memory available {

cerr << "*** Inadequate memory ***\n";

exit(-1);

}

myCapacity_ = original.myCapacity_;// copy myCapacity_

myTop_ = original.myTop_ ; // copy myTop_ member // copy array member for (int pos = 0; pos < myCapacity_; pos++)

myArrayPtr[pos] = original.myArrayPtr[pos];

}

return *this; // return reference to this object }

(39)

39

//***** Test Driver ***********

#include <iostream>

using namespace std;

#include "RTStack.h"

Print (Stack<int> st) {

cout << st;

}

int main() {

int Size;

cout << "Enter stack size: ";

cin >> Size;

Stack<int> S(Size);

for (int i = 1; i <= 5; i++) S.push(i)

Stack<int> T = S;

cout << T << endl;

}

Sample Runs:

Enter stack capacity: 5 5

4 3 2 1

--- Enter stack capacity: 3

*** Stack is full -- can't add new value ***

*** Stack is full -- can't add new value ***

3 2 1

--- Enter stack capacity: 0

StackRT.cc:12: failed

assertion `NumElements > 0' Abort

(40)

40

See Figure 8.7 on p. 440

Test driver with statements in the constructor, copy

constructor, and destructor to trace when they are called.

(41)

41

Linked Lists

As an abstract data type,

a list is a finite sequence (possibly empty) of elements with basic operations that vary from one application to another.

Basic operations commonly include:

Construction: Allocate and initialize a list object (usually empty) Empty: Check if list is empty

Traverse: Visit each element of the list in order stored Insert: Add an item to the list (at any point)

Delete: Remove an item from the list (at any point)

(42)

42

Linked Lists

Minimal requirements:

1. Locate the first element.

2. Given the location of any list element, find its successor.

3. Determine if at the end of the list.

For the array/vector-based implementation:

1. first element is at location 0

2. Successor of item at location i is at location i + 1 3. At location size – 1

A gross inefficiency is caused by #2 -- insertions and deletions requires one to shift elements;

Remove requirement that list elements be stored in consecutive location

 need a "link" that connects each element to its successor

 linked lists

(43)

43

Linked Lists

A l i n k e d l i s t i s a n o r d e r e d c o l l e c t i o n o f e l e m e n t s c a l l e d n o d e s e a c h o f w h i c h h a s t w o p a r t s :

( 1 ) D a t a p a r t : S t o r e s a n e l e m e n t o f t h e l i s t ;

( 2 ) N e x t p a r t : S t o r e s a l i n k ( p o i n t e r ) t o t h e n e x t l i s t e l e m e n t . I f t h e r e i s n o n e x t e l e m e n t , a s p e c i a l n u l l v a l u e i s u s e d . A d d i t i o n a l l y , a n e x t e r n a l l i n k m u s t b e m a i n t a i n e d :

t h a t t o t h e l o c a t i o n o f t h e n o d e s t o r i n g t h e f i r s t l i s t e l e m e n t , T h i s v a l u e w i l l b e n u l l , i f t h e l i s t i s e m p t y .

E x a m p l e : A l i n k e d l i s t s t o r i n g 9 , 1 7 , 2 2 , 2 6 , 3 4 :

9 17 22 26 34

first data

next

(44)

44

Linked List Basic Operations

Construction: first = null_value;

Empty: return (first == null_value)

Traverse: ptr = first;

while (ptr != null_value)

{ Process data part of

node pointed to by ptr;

ptr = address of next element;

// contained in next part of

// node pointed to by ptr;

}

Insert & Delete: Both operations necessitate the changing of links.

The address of the predecessor of the node to be added or removed needs to be known.

(45)

45

Linked List Traversal

9 17 22 26 34

first

ptr

9 17 22 26 34

first

ptr

...

9 17 22 26 34

first

ptr

9 17 22 26 34

first

ptr

(46)

46

Linked List Insert

T o i n s e r t 2 0 a f t e r 1 7 i n t h e p r e c e d i n g l i n k e d l i s t

N e e d a d d r e s s o f i t e m b e f o r e p o i n t o f i n s e r t i o n

p r e d p t r p o i n t s t o t h e n o d e c o n t a i n i n g 1 7

( 1 ) G e t a n e w n o d e p o i n t e d t o b y n e w p t r a n d s t o r e 2 0 i n i t

20

9 17 22 29 34

predptr

newptr first

( 2 ) S e t t h e n e x t p o i n t e r o f t h i s n e w n o d e e q u a l t o t h e n e x t p o i n t e r i n p r e d p t r t h u s m a k i n g i t p o i n t t o i t s s u c c e s s o r .

20

9 17 22 29 34

predptr

newptr first

(47)

47

Linked List Insert

( 3 ) R e s e t t h e n e x t p o i n t e r o f p r e d p t r t o p o i n t t o n e w p t r

20

9 17 22 29 34

predptr

newptr first

S a m e p r o c e s s w o r k s a t t h e e n d o f t h e l i s t , e . g . i n s e r t 5 5 . ( 1 ) a s b e f o r e

( 2 ) a s b e f o r e — s e t s n e x t l i n k t o n u l l p o i n t e r ( 3 ) a s b e f o r e

9 17 22 29 34

first 20

55 predptr

newptr

(48)

48

Linked List Insert

I n s e r t i n g a t t h e b e g i n n i n g o f t h e l i s t r e q u i r e s a m o d i f i c a t i o n o f s t e p 3 : E x a m p l e : I n s e r t a n o d e c o n t a i n i n g 5 a t t h e b e g i n n i n g o f t h e l i s t .

( 1 ) a s b e f o r e

( 2 ) s e t s n e x t l i n k t o f i r s t n o d e i n t h e l i s t ( 3 ) s e t f i r s t t o p o i n t t o n e w n o d e .

9 17 22 29 55

first 20

5 predptr

newptr

34

(49)

49

Linked List Delete

D e l e t e 2 2 : N e e d p r e d e c e s s o r i n l i n k e d l i s t

( 1 ) D o a b y p a s s o p e r a t i o n : S e t t h e n e x t p o i n t e r i n t h e p r e d e c e s s o r t o p o i n t t o t h e s u c c e s s o r o f t h e n o d e t o b e d e l e t e d

5 17 22 29 34

first 9 20

predptr ptr

( 2 ) D e a l l o c a t e t h e n o d e b e i n g d e l e t e d .

5 17 22 29 34

first 20

free store

9

predptr ptr

(50)

50

Linked List Delete

S a m e p r o c e s s w o r k s f o r d e l e t i n g t h e e n d o f t h e l i s t , e . g . d e l e t e 3 4 . ( 1 ) a s b e f o r e — s e t s n e x t l i n k t o n u l l p o i n t e r

( 2 ) a s b e f o r e

5 17 22 29

first 9

predptr ptr

34

free store

D e l e t i n g a t t h e b e g i n n i n g o f t h e l i s t r e q u i r e s a m o d i f i c a t i o n o f s t e p 1 : ( 1 ) r e s e t f i r s t

( 2 ) a s b e f o r e

E x a m p l e : D e l e t e 5 f r o m t h e p r e v i o u s l i s t

5 17 22 29

first 9

predptr ptr

(51)

51

Linked List

Advantanges of linked lists:

store items "sequentially" without restrictions on location

access any item as long as external link to first item maintained

insert new item without shifting

delete existing item without shifting

size can expand/contract throughout use Disadvantages

overhead of links: used only internally, pure overhead

if dynamic, must provide destructor, copy constructor

no longer have direct access to each element of the list

O(1) access becomes O(n) access since we must go through first element, and then second, and then third, etc.

List-processing algorithms that require fast access to each element cannot (usually) be done as efficiently with linked lists, e.g.:

Binary search

Sorting

Append item (add to end of list)

(52)

52

Linked List Implementation

//node definition class Node

{ public:

DataType data;

Node *next;

};

Node is a recursive (or self-referential) definition because it uses the name Node in its definition:

the next member is defined as a pointer to a Node.

// link definition

Node *ptr; or typedef Node *NodePointer;

NodePointer ptr;

//Allocate // Deallocate:

ptr = new Node; delete ptr;

(53)

53

Linked List Implementation

To access the data and next part of node:

(*ptr).data and (*ptr).next or better, use the -> operator

ptr->data and ptr->next

Why make data members public in class Node? (Node could also be a struct) This class declaration will be placed inside class declaration for LinkedList.

Data members data and next of struct Node will be public inside LL class and thus will accessible to the member and friend functions of the class,

but they will be private outside the class.

(54)

54

Linked List Implementation

#ifndef LINKEDLIST

#define LINKEDLIST typedef int DataType;

class LinkedList {private:

class Node public:{

DataType data;

Node * next;

};

typedef Node * NodePointer;

. . . };#endif

// could also define a template taking DataType as its type // parameter and thus avoid the recompilation required when // typedef changes

References

Related documents

However, landing centres in East Godavari, West Godavari and Prakasam districts showed decrease due to disturbance to fishing activities by industrial/other activities.The

Table 8 presents impact estimates where the individual is treated as the unit of analysis (as is the case for all other.. Note: Estimated impacts per Job Corps participant

Components of a Cluster MySQL (mysqld) NDB Application .Net JBoss PHP Management (ndb_mgmd) SQL Nodes Data Nodes Management Nodes ndbd ndbd ndbd ndbd Data Nodes MySQL (mysqld) NDB

EnduroSat's 1U solar panels provide three connectors for power output from the solar cells, sensor communication and magnetorquer control:. • H1 - Output Power Bus Connector • H2

This book explores Spark as well as the other components of the Berkeley Data Analytics Stack (BDAS), a data processing alternative to Hadoop, especially in the realm of big

Protection of first-aiders : If potential for exposure exists refer to Section 8 for specific personal protective equipment.. Notes to physician :

opportunities for municipalities and implementers on issues such as effective alternatives to motorized transportation, cost effectiveness, sound land use principles, and

(14) Our investigation also showed that patients with sympto- matic subacromial impingement syndrome achieved significant improvement in shoulder pain and range of motion