Slide Set 14 - for ENCM 339 Fall 2014 Lecture Section...

41
Slide Set 14 for ENCM 339 Fall 2014 Lecture Section 01 Steve Norman, PhD, PEng Electrical & Computer Engineering Schulich School of Engineering University of Calgary Fall Term, 2014

Transcript of Slide Set 14 - for ENCM 339 Fall 2014 Lecture Section...

Slide Set 14for ENCM 339 Fall 2014 Lecture Section 01

Steve Norman, PhD, PEng

Electrical & Computer EngineeringSchulich School of Engineering

University of Calgary

Fall Term, 2014

ENCM 339 F14 Section 01 Slide Set 14 slide 2/41

Contents

More about dynamic allocation in C++

References to pointers

Scopes of function parameters and local variables

Destructor functions

A class with dynamic memory management

ENCM 339 F14 Section 01 Slide Set 14 slide 3/41

Outline of Slide Set 14

More about dynamic allocation in C++

References to pointers

Scopes of function parameters and local variables

Destructor functions

A class with dynamic memory management

ENCM 339 F14 Section 01 Slide Set 14 slide 4/41

More about dynamic allocation in C++

The next several slides continue the discussion of C++ dynamicmemory allocation from the end of the previous slide set.

In the previous slide set, there were a number of examples thatresembled the function definition on the next slide.

Simple examples like that are good for demonstrating thesyntax (grammar) and semantics (meanings) of expressionsinvolving new and delete.

But such examples make poor suggestions about programdesign with dynamic allocation!

ENCM 339 F14 Section 01 Slide Set 14 slide 5/41

void f(int arg) {

Bar *p = new Bar;

double *q = new double[arg];

// Do some work with the Bar object

// and the array ...

delete p;

delete [] q;

}

Having functions allocate and deallocate dynamic memory in adisorganized way is a risk factor for hard-to-fix programdefects.

In C++ it is better to move dynamic allocation anddeallocation into classes that have well-designed policies formemory management. We’ll look at that in more detail soon.

ENCM 339 F14 Section 01 Slide Set 14 slide 6/41

Consider using library container classes!

Library container classes, such as string and vector types,and others, such as list, set, and map types, all managedynamically allocated memory, and use sophisticatedalgorithms to smoothly handle growth or shrinkage of thenumber of data items in any particular container.

In practical C++ projects, look for suitable librarycontainers—you can probably avoid the hard work ofdesigning and implementing policies for use of new anddelete in your own code.

But in ENCM 339, we’ll ignore the above advice for a littlewhile! Writing our own classes that use new and delete willgive us a lot of insight into how the library container typeswork.

ENCM 339 F14 Section 01 Slide Set 14 slide 7/41

Common mistakes with dynamic allocation in C++

I choice of delete-without-[ ] or delete-with-[ ] does notmatch earlier use of new;

I attempt to access an object or array element that hasbeen deleted;

I attempt to delete an object or array that has already beendeleted;

I attempt to delete an object or array that is not in theheap;

I address used with delete-with-[ ] is the address of anarray element that is not element 0;

I memory leaks—failure to delete dynamically-allocatedmemory when it is no longer in use.

ENCM 339 F14 Section 01 Slide Set 14 slide 8/41

Unfortunately the effects of these mistakes are hard topredict!

The effects depend on how the heap manager works in aparticular library, and the history of allocations anddeallocations made in a particular program run.

ENCM 339 F14 Section 01 Slide Set 14 slide 9/41

What happens if new fails to allocate memory?

The response to an invocation of new can be modeled as asequence of two steps.

Step 1. Call a function similar to the C malloc function tofind a big-enough chunk of contiguous bytes in the heap.

Step 2. Apply appropriate C++ initialization rules for thenewly-allocated bytes.

But what if Step 1 fails?

In older implementations of C++ the value of the new

expression would be a null pointer. But in currentimplementations, the result is a thing called a bad_alloc

exception.

ENCM 339 F14 Section 01 Slide Set 14 slide 10/41

Explaining how a bad_alloc exception works would require along digression about C++ exception handling.

In ENCM 339, you can just assume that if new fails to findsufficient memory, there will be a bad_alloc exception, theend result of which will be a reasonably orderly program crash.

In ENCM 339, do not check to see whether use of new hasproduced a null pointer.

ENCM 339 F14 Section 01 Slide Set 14 slide 11/41

Outline of Slide Set 14

More about dynamic allocation in C++

References to pointers

Scopes of function parameters and local variables

Destructor functions

A class with dynamic memory management

ENCM 339 F14 Section 01 Slide Set 14 slide 12/41

References to pointers

This section is out-of-order with respect to course material.I placed it here to help out with a Lab 7 exercise.

It is possible and sometimes useful to create a reference to apointer.

For example, the type int*& is read “reference to pointerto int”.

The referent of such a thing would be a variable of typepointer-to-int, just as the referent of an int& must be avariable of type int.

ENCM 339 F14 Section 01 Slide Set 14 slide 13/41

#include <iostream>

int main()

{

int a = 22, b = 33;

int *p = &a;

int *&r = p;

*p += 2;

r = &b; // (1)

*p = a + 20;

std::cout << *r << std::endl;

return 0;

}

Let’s make a diagram for point (1) and determine the programoutput.

ENCM 339 F14 Section 01 Slide Set 14 slide 14/41

Outline of Slide Set 14

More about dynamic allocation in C++

References to pointers

Scopes of function parameters and local variables

Destructor functions

A class with dynamic memory management

ENCM 339 F14 Section 01 Slide Set 14 slide 15/41

Scopes of function parameters and local variables

To understand how C++ class destructor functions work, it’snecessary to understand

I what is meant by the scope of a parameter or localvariable;

I what it means for a parameter or local variable to “comeinto scope” and “go out of scope”.

But first, I need to be clear about the term parameter. I’ll tryvery hard to be precise with the words parameter andargument, at least with respect to this slide set . . .

ENCM 339 F14 Section 01 Slide Set 14 slide 16/41

arguments and parameters

// Program valid in

// both C and C++!

int sqr(int n);

int main(void) {

int s;

s = sqr(5);

// ...

}

int sqr(int n) {

return n * n;

}

Both ENCM 339 textbooks wouldsay that 5 is an argument and n isa parameter.

Other books call 5 an actualargument and n a formalargument.

Still other books—and yourinstructor—are often sloppy aboutthe distinction, and use the wordargument for both kinds of thing.

ENCM 339 F14 Section 01 Slide Set 14 slide 17/41

scope

In discussion of computer programming, the term scopegenerally refers to the region(s) within a program source codefile where a specific name can be used.

Before moving on to parameters and local variables, let’s takea quick look at an example involving a global variable . . .

}

int foo(int x);

int bar(int y);

void quux() {// ...

int glob = 22;

int main() {// ...

}

}

int foo(int x) {

int bar(int y) {

// ...

scope ofglob

}// ...

The name of the globalvariable glob can’t beused in the definition ofquux, but can be usedin the other threefunction definitions.

ENCM 339 F14 Section 01 Slide Set 14 slide 19/41

coming into and going out of scope

In discussion of C++ these phrases refer specifically toparameters and local variables of functions.

A function parameter comes into scope when its functionbecomes active, and goes out of scope when the functionreturns.

A local variable comes into scope at its point of declaration,and goes out of scope when its enclosing block ends or itsfunction definition returns, whichever happens first.

In the program on the next slide, how many times do theparameter n and all the local variables of sumsqr come intoand go out of scope, in each of the two calls to sumsqr ?

ENCM 339 F14 Section 01 Slide Set 14 slide 20/41

int sumsqr(int n) {

if (n <= 0)

return 0;

int result = 0, i = 1;

while (i <= n) {

int sqr = i * i;

result += sqr;

i++;

}

return result;

}

int main() {

int a = sumsqr(0);

int b = sumsqr(3);

// ...

}

ENCM 339 F14 Section 01 Slide Set 14 slide 21/41

Scope and for statements

Here is the general syntax for a C++ for statement:

for ( init ; condition ; update )

statement

Remember, statement could be an expression followed by asemicolon, a block starting with { and ending with }, or one ofa few other kinds of things.

There is a special rule for the scope of any variable(s)

declared in the init part of a for statement.

The rule is most easily explained with an example . . .

ENCM 339 F14 Section 01 Slide Set 14 slide 22/41

void foo(int n) {

for (int i = 0; i < n; i++) {

int k = 0;

cout << i << ’ ’ << k << endl;

}

// (1)

cout << n << endl;

}

Suppose that the parameter n has a value of 3.

Q1. What output is produced by foo?

Q2. How many times do i and k come into and go out ofscope?

Q3. Would the output be the same if we replacedcout << n << endl; with cout << i << endl; ?

ENCM 339 F14 Section 01 Slide Set 14 slide 23/41

Outline of Slide Set 14

More about dynamic allocation in C++

References to pointers

Scopes of function parameters and local variables

Destructor functions

A class with dynamic memory management

ENCM 339 F14 Section 01 Slide Set 14 slide 24/41

Destructor functions

A destructor function is a special kind of class memberfunction. The purpose of a destructor is to releaseresources managed by a class object at the end of thelifetime of that class object.

By far the most common use of destructors in C++programs is the proper deallocation of heap memory managedby class objects.

A common abbreviation for “destructor” is dtor (much likector is short for “constructor”).

ENCM 339 F14 Section 01 Slide Set 14 slide 25/41

The name of a destructor the name of the class preceded bythe ~ character (“tilde”, or “squiggle”).

A class can have no more than one destructor.

The destructor has no explicit parameters. It does have animplicit this parameter.

There is no return type, not even void.

Suppose that Foo is a class with a destructor. Let’s sketch theclass definition, including a function prototype for thedestructor.

ENCM 339 F14 Section 01 Slide Set 14 slide 26/41

When are destructors called?

For a function parameter of class type or a non-staticlocal variable (the normal kind of local variable) of classtype, the destructor is called when the parameter or variablegoes out of scope.

For a dynamically allocated object or array element, thedestructor is called as part of the deallocation that followsfrom use of delete-without-[ ] or delete-with-[ ].

For a statically allocated class object, the exact rules aremessy, but roughy speaking the answer is “sometime afterreturn from main.”

To understand these ideas more clearly, let’s look at a classthat does very little but trace constructor and destructorcalls . . .

ENCM 339 F14 Section 01 Slide Set 14 slide 27/41

// File TracerV1.h

#ifndef TRACERV1_H

#define TRACERV1_H

#include <iostream>

using std::cout; using std::endl;

class TracerV1 {

public:

TracerV1(int init = 0) : dataM(init) {

cout << "ctor " << init << endl;

}

~TracerV1() { cout << "dtor " << dataM << endl; }

int data() const { return dataM; }

void inc() { dataM++; }

private:

int dataM;

};

#endif

ENCM 339 F14 Section 01 Slide Set 14 slide 28/41

V1 stands for Version 1. Version 2 will add two moreimportant member functions.

Let’s draw a diagram for point (1), and write down theprogram output . . .

#include "TracerV1.h"

int main() {

cout << "main just started" << endl;

TracerV1 t;

t.inc();

t.inc(); // (1)

cout << "data in y is " << t.data() << endl;

return 0;

}

That was easy, I hope. What is the output of the morecomplicated program on the next slide?

ENCM 339 F14 Section 01 Slide Set 14 slide 29/41

#include "TracerV1.h"

TracerV1 glob(100); // WARNING! See next slide!

int main() {

cout << "main just started" << endl;

TracerV1 t1(200);

cout << "about to enter nested block" << endl;

{

TracerV1 t2(300);

t2.inc();

}

cout << "out of nested block" << endl;

t1.inc();

cout << "about to return from main" << endl;

return 0;

}

ENCM 339 F14 Section 01 Slide Set 14 slide 30/41

Why did I need to provide a WARNING about the global classobject glob?

The constructor and destructor of TracerV1 both make use ofthe global class object cout.

So the program depended on the constructor for glob beingcalled after the constructor for cout, and the destructor forglob being called before the destructor for cout.

Both “right things” happened when I built an executable andran it on my own Linux machine.

But in general it is asking for trouble to depend on the orderof construction or destruction of global objects.

ENCM 339 F14 Section 01 Slide Set 14 slide 31/41

Here is an example with function parameters.What is the output, and why?

#include "TracerV1.h"

void foo(TracerV1 par1, TracerV1& par2) {

par1.inc();

par2.inc();

}

int main() {

TracerV1 t1(80), t2(900);

cout << "calling foo" << endl;

foo(t1, t2);

cout << "back from foo" << endl;

return 0;

}

ENCM 339 F14 Section 01 Slide Set 14 slide 32/41

Finally, dynamic memory allocation and deallocation.What is the output, and why?

#include "TracerV1.h"

int main() {

cout << "main just started" << endl;

TracerV1 *p = new TracerV1[2];

p[1].inc();

delete [ ] p;

cout << "main is about to return" << endl;

return 0;

}

What insight does this example give about the need to choosecorrectly between delete-without-[ ] and delete-with-[ ] ?

ENCM 339 F14 Section 01 Slide Set 14 slide 33/41

Outline of Slide Set 14

More about dynamic allocation in C++

References to pointers

Scopes of function parameters and local variables

Destructor functions

A class with dynamic memory management

ENCM 339 F14 Section 01 Slide Set 14 slide 34/41

A class with dynamic memory management

Now that we’ve seen new and delete, reviewed the idea ofscope, and learned about destructors, we’re ready to writecode for a class where class objects will manage dynamicallyallocated memory.

We’ll call the class BadString. The constructors and thedestructor for the class will actually be good, as will amember function called push_back.

The “Bad” aspect of this class has to do with copying ofclass objects—we’ll look at that in the next slide set.

Before looking at code for BadString, we need to reviewsome things we’ll use in writing that code: null pointervalues and conditional expressions.

ENCM 339 F14 Section 01 Slide Set 14 slide 35/41

Null pointers in C and C++

The concept of a null pointer is exactly the same in C andC++—it’s an address that is definitely not the address of anykind of data.

A null pointer is not the same thing as an uninitializedpointer, nor is it the same thing as what is sometimes calleda “dangling pointer”. Let’s make a few notes about thedistinctions.

In both C and C++, 0 is the only constant that is both an int

constant and a pointer constant. As a pointer, 0 representsthe null pointer value.

ENCM 339 F14 Section 01 Slide Set 14 slide 36/41

Several C library headers define the macro NULL as equivalentto the null pointer value. More often than not, C source codeuses NULL, not 0, to specify a null pointer value.

C++ library headers generally do not define NULL.

For C++98 and earlier versions of C++, the constant 0 is thepreferred way to specify a null pointer value. So that’s whatwe’ll do for C++ code in ENCM 339 this term.

C++11 has introduced the keyword nullptr, which is helpful,and should be used if you know that your code will be alwaysbe compiled with a C++11-compatible compiler.

ENCM 339 F14 Section 01 Slide Set 14 slide 37/41

Dynamic deallocation and null pointers

In both C and C++ an attempt at dynamic deallocation usinga null pointer is safe and has no real effect other than the lossof a very tiny amount of time.

For example, in C, this statement is safe and does nothing:

free(NULL);

Similarly, in C++, both

delete 0;

and

delete [ ] 0;

are guaranteed to be safe and to have no effect on theorganization of the heap.

ENCM 339 F14 Section 01 Slide Set 14 slide 38/41

Conditional expressions

These work in both C and C++.

The value of the expression

condition ? expr1 : expr2

is equal to the value of expr1 if condition is true, and equal

to the value of expr2 if condition is false.

The term conditional operator is sometimes used as a namefor this kind of use of a question mark and a colon.

Advice from your instructor:

I only write conditional expressions that are really simple;

I use parentheses to help make meanings clear.

ENCM 339 F14 Section 01 Slide Set 14 slide 39/41

Memory management policy for BadString

objects

The BadString class definition will declare three private

member variables:

char *storageM;

int lengthM;

int capacityM;

For empty BadString objects—that is, objects that representstrings of zero length—storageM will be a null pointer, andlengthM and capacityM will both have values of zero.

Let’s write down a policy for the member variables ofnon-empty BadString objects.

The next slide shows a header file for the BadString class . . .

#ifndef BADSTRING_H

#define BADSTRING_H

class BadString {

public:

BadString() : storageM(0), lengthM(0), capacityM(0)

{ }

BadString(const char *s);

~BadString() { delete [ ] storageM; }

const char *c_str() const {

return (lengthM == 0) ? "" : storageM;

}

void push_back(char c);

// Functions for length, append, element access, ...

private:

char *storageM;

int lengthM;

int capacityM;

};

#endif

ENCM 339 F14 Section 01 Slide Set 14 slide 41/41

At the beginning of the next slide set, we’ll look carefully at

I definitions of the constructors and the destructor ofBadString ;

I definitions of the member functions c_str andpush_back.