+ All Categories
Home > Documents > Object Oriented Programming COP3330 / CGS5409. Dynamic Allocation in Classes Review of CStrings.

Object Oriented Programming COP3330 / CGS5409. Dynamic Allocation in Classes Review of CStrings.

Date post: 26-Dec-2015
Category:
Upload: caroline-bailey
View: 224 times
Download: 1 times
Share this document with a friend
Popular Tags:
34
Object Oriented Programming COP3330 / CGS5409
Transcript

Object Oriented ProgrammingCOP3330 / CGS5409

Dynamic Allocation in Classes Review of CStrings

Allocate dynamic space with operator new, which returns address of the allocated item. Store in a pointer:

int * ptr = new int; // one dynamic integerdouble * nums = new double[size];

// array of doubles, called "nums"

Clean up memory with operator delete. Apply to the pointer. Use delete [] form for arrays:

delete ptr; // deallocates the integer abovedelete [] nums; // deallocates double array above

Remember that to access a single dynamic item, dereference is needed:

cout << ptr; // prints pointer contentscout << *ptr; // prints the target

For a dynamically created array, the pointer attaches to the starting position of the array, so can act as the array name:

nums[5] = 10.6;cout << nums[3];

Just like basic types, objects can be allocated dynamically.

When an object is created, the constructor runs. Default constructor is invoked unless parameters are added:

Fraction * fp1, * fp2, * flist;fp1 = new Fraction; // uses default

// constructorfp2 = new Fraction(3,5); // uses constructor with

// two parameters

flist = new Fraction[20]; // dynamic array of 20 // Fraction objects

Default constructor used on each

Just like basic types, dynamically created objects must also be de-allocated.

De-allocation with delete works the same as for basic types:

delete fp1;delete fp2;delete [] flist;

Dot-operator requires an object name (or effective name) on the left side

objectName.memberName// member can be data or function

The arrow operator works similarly as with structures.

pointerToObject->memberName

Remember that if you have a pointer to an object, the pointer name would have to be dereferenced first, to use the dot-operator:

(*fp1).Show();

Arrow operator is a nice shortcut, avoiding the use or parentheses to force order of operations:

fp1->Show(); //equivalent to (*fp1).Show();

When using dynamic allocation of objects, we use pointers, both to single object and to arrays of objects. Here's a good rule of thumb:

For pointers to single objects, arrow operator is easiest:

fp1->Show();fp2->GetNumerator();fp2->Input();

For dynamically allocated arrays of objects, the pointer acts as the array name, but the object "names" can be reached with the bracket operator. Arrow operator usually not needed:

flist[3].Show();flist[5].GetNumerator();

Note that this would be INCORRECT, flist[2] is an object, not a pointer!

flist[2]->Show();

A motivating example Suppose we want an array as member data of a

class, but we don't want to be stuck with a fixed upper bound on the size.

How do we accomplish this?

The solution would be to use dynamic allocation on the array. But how to arrange it?

Can a dynamic array be physically embedded inside a class?

What if an object of that class type is created statically? ◦ Then the compiler has to know the size. But the internal

contents are to be dynamic?!

A motivating example

Solution: Only the pointer will be in the member data

section. Dynamic memory will not be physically in

the object, but only linked by the member data pointer.

To set up dynamic memory as contents of an object, declare one or more pointers as member data

Always initialize pointers in the constructor! ◦ Constructor might go ahead and dynamically allocate

space right away, assigning addresses to pointers.◦ If not allocating space right away, best to initialize to

null pointer until ready for use Use new inside class member functions to

allocate space, attaching space to pointers (could be in constructors, or other member functions)

Make sure to use delete to clean up dynamically allocated space whenever finished using it ◦ This could happen in regular member functions,

wherever space is cleared◦ This should happen in the destructor, because

this is guaranteed to be the last function that runs for an object!

Separate memory management tasks from the functionality/algorithmic tasks wherever possible: ◦ Write a set of member functions just for dealing

with memory management issues ◦ -- like creation of space, deallocation, resizing, etc◦ Your algorithmic functions can call the memory-

handling functions, when needed◦ The more uses of new and delete there are in a

class, the more complicated it gets, and the more chances of a memory leak.

◦ Separation of tasks helps minimize this.

Code Example: PhoneBook Database Simulation The classes are Entry and Directory.

◦ Entry -- An object of this class type represents a single entry in a phone book.  The data members stored in an entry object are name, address, and phone number.  Strings (i.e. null-terminated character arrays) are used to store these items.

◦ Directory -- An object of type Directory stores a list of Entry objects, using a dynamic array.  The Directory class also provides services (public member functions) for adding new entries, deleting entries, modifying entries, searching for entries, and displaying all entries in the phone book.  The Directory class also has a function for dynamically resizing the array of Entries when more memory space is needed.

Note that in this class, the destructor is also implemented for the Directory class, with a needed definition inside.

Since the member data of an object of type Directory includes a pointer, which is being used to point to dynamically allocated space (i.e. the array of entries), it is our job in the code to deallocate that space.

When the object is deallocated, the compiler only automatically gives up the space inside the object.

The pointer entryList is pointing to data that is physically outside the object, so it doesn't get automatically "cleaned up".

But, we can clean up this space (before the object goes away) by doing it in the last function that runs for an object (which is always the destructor).

Note that the definition of this destructor is:

delete [] entryList;

This simply deallocates the dynamic array attached to entryList, before we let this pointer be deallocated along with the object.

Code Example: Second version of Phone Book example

This one contains overloads of operator<< and operator>> in class Entry,

instead of Show() and Load()

Recall that a C-style string is a character array that ends with the null character

Character literals in single quotes◦ 'a', '\n', '$'

string literals in double quotes◦ "Hello World\n"◦ Remember that the null-character is implicitly a part of

any string literal The name of an array acts as a pointer to the

first element of an array (i.e. it stores the address of where the array starts)◦ Recall that this means when an array is passed into a

function, the function has access to the original array contents

Conversion functions: These return the ascii value of a character ◦ int toupper(int c) - returns the uppercase version of c if it's a lowercase

letter, otherwise returns c as is◦ int tolower(int c) - returns the lowercase version of c if it's an uppercase

letter, otherwise returns c as is Query Functions: These all return true (non-zero) or false (0),

in answer to the question posed by the function's name. They all take in the ascii value of a character as a parameter. ◦ int isdigit(int c) - decides whether the parameter is a digit (0-9)◦ int isalpha(int c) - decides whether the character is a letter (a-z, A-Z)◦ int isalnum(int c) - digit or a letter?◦ int islower(int c) - lowercase digit? (a-z)◦ int isupper(int c) - uppercase digit? (A-Z)◦ int isxdigit(int c) - hex digit character? (0-9, a-f)◦ int isspace(int c) - white space character?◦ int iscntrl(int c) - control character?◦ int ispunct(int c) - printing character other than space, letter, digit?◦ int isprint(int c) - printing character (including ' ')? ◦ int isgraph(int c) - printing character other than ' ' (space)?

Recall that in the special case of arrays of type char, which are used to implement c-style strings, we can use these special cases with the insertion and extraction operators:

char greeting[20] = "Hello, World"; cout << greeting; // prints "Hello, World"

char lastname[20]; cin >> lastname; // read string into lastname

// adds the null character automatically

There are two more member functions in class istream (in the iostream library), for reading and storing C-style strings into arrays of type char. Here are the prototypes:

char* get(char str[], int length, char delimiter = '\n'); char* getline(char str[], int length, char delimiter = '\n');

Note that this get function is different than the two versions of get we've already seen, which were for reading single characters from an input stream:

char ch; ch = cin.get();// extracts one character, returns it cin.get(ch); // extracts one character, stores in ch

The functions get and getline (with the three parameters) will read and store a c-style string. The parameters:◦ First parameter (str) is the char array where the data will be

stored. Note that this is an array passed into a function, so the function has access to modify the original array

◦ Second parameter (length) should always be the size of the array -- i.e. how much storage available.

◦ Third parameter (delimiter) is an optional parameter, with the newline as the default. This is the character at which to stop reading

Both of these functions will extract characters from the input stream, but they don't stop at any white space -- they stop at the specified delimiter. They also automatically append the null character, which must (as always) fit into the size of the array.

Sample calls:

char buffer[80];

cin >> buffer; // reads one word into buffercin.get(buffer, 80, ','); // reads up to the first comma, // stores in buffer

cin.getline(buffer, 80); // reads an entire line (up to // newline)

So what is the difference between get and getline?

get will leave the delimiter character on the input stream, and it will be seen by the next input statement

getline will extract and discard the delimiter character

char greeting[15], name[10], other[20];

cin.getline(greeting,15); // gets input into the greeting array cin.get(name,10,'.'); // gets input into the name array cin.getline(other,20); // gets input into the other array

Suppose that the data on the input stream (i.e. typed onto the keyboard, for instance) is:

Hello, World Joe Smith. He says hello.

At this point, the contents of each string are:

greeting: "Hello, World" name: "Joe Smith" other: ". He says hello."

The standard string library in C is called cstring. To use it, we place the appropriate #include statement in a code file:

#include <cstring>

This string library contains many useful string manipulation functions.

strlen, strcpy, strcat, strcmp

Takes one string argument, returns its length (not counting the null character)

Prototype: int strlen(const char str[]); Sample calls: char phrase[30] = "Hello, World"; cout << strlen("Greetings, Earthling!"); // print 21 int length = strlen(phrase); // stores 12

Takes two string arguments, copies the contents of the second string into the first string. The first parameter is non-constant, the second is constant

Prototype: char* strcpy(char str1[], const char str2[]); Sample calls: char buffer[80], firstname[30], lastname[30] = "Smith";

strcpy(firstname, "Billy Joe Bob"); strcpy(buffer, lastname); cout << firstname; // prints "Billy Joe Bob" cout << buffer; // prints "Smith"

• Takes two string arguments (both passed as const arrays), and returns an integer that indicates their lexicographic order

• Prototype: • int strcmp(const char str1[], const char str2[]); • returns: • a negative number, if str1 comes before str2 • a positive number, if str2 comes before str1 • 0 , if they are equal

• Note: Lexicographic order is by ascii codes. It's NOT the same as alphabetic order!

• Sample calls: • char word1[30] = "apple"; • char word2[30] = "apply"; • if (strcmp(word1, word2) != 0) • cout << "The words are different\n"; • strcmp(word1, word2) // returns a negative,

// word1 comes first • strcmp(word1, "apple") // returns a 0. strings

are // the same • strcmp("apple", "Zebra") // returns a positive.

• "Zebra" comes first! (all uppercase before lowercase in ascii)

• Note that the previous calls rely on the null character as the terminator of C-style strings. Remember, there is no built-in bounds checking in C++

• strncpy, strncat, strncmp - these do the same as the three listed above, but they take one extra argument (an integer N), and they go up to the null character or up to N characters, whichever is first.

• Therefore, are safer options. The extra parameter can be included to guarantee that array boundaries are not exceeded, as seen in the last strncpy example

• Example uses:char buffer[80];

char word[11] = "applesauce";

char bigword[] = "antidisestablishmentarianism"; strncpy(buffer, word, 5); // buffer now stores "apple"

strncat(buffer, " piecemeal", 4);// buffer now "apple pie"

strncmp(buffer, "apple", 5); // returns 0, as first 5

// characters of the strings are equal

strncpy(word, bigword, 10); // word is now "antidisest"

// word only had 11 slots!


Recommended