340 likes | 509 Views
Recitation Week 8. Object Oriented Programming COP3330 / CGS5409. Today’s Recitation. Dynamic Allocation in Classes Review of CStrings. Dynamic Allocation & Cleanup. Allocate dynamic space with operator new, which returns address of the allocated item. Store in a pointer:
E N D
Recitation Week 8 Object Oriented Programming COP3330 / CGS5409
Today’s Recitation • Dynamic Allocation in Classes • Review of CStrings
Dynamic Allocation & Cleanup • Allocate dynamic space with operator new, which returns address of the allocated item. Store in a pointer: int* ptr = new int; // one dynamic integer double * 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 above delete [] nums; // deallocatesdouble array above
Accessing Dynamic Items • Remember that to access a single dynamic item, dereference is needed: cout<< ptr; // prints pointer contents cout<< *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];
Dynamic Allocation of Objects • 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 // constructor fp2 = new Fraction(3,5); // uses constructor with // two parameters flist= new Fraction[20]; // dynamic array of 20 // Fraction objects • Default constructor used on each
Dynamic Object De-allocation • 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 vs. Arrow-Operator • 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
Dot-Operator vs. Arrow-Operator • 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();
Dynamically Allocated Objects • 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();
Dynamically Allocated Arrays • 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();
Dynamic Allocation in Classes • 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?!
Dynamic Allocation in Classes • 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.
Dynamic allocation inside classes • 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)
Dynamic Allocation in Classes • 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!
Good Design Principles • 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.
Dynamic Allocation in Classes • 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.
PhoneBook Database Simulation • 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.
PhoneBook Database Simulation • 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.
PhoneBook Database Simulation • Code Example: • Second version of Phone Book example • This one contains overloads of operator<< and operator>> in class Entry, • instead of Show() and Load()
Characters, Strings, and the cstringlibrary Recap • 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
Recap: the cctype library • Conversion functions: These return the ascii value of a character • inttoupper(int c) - returns the uppercase version of c if it's a lowercase letter, otherwise returns c as is • inttolower(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. • intisdigit(int c) - decides whether the parameter is a digit (0-9) • intisalpha(int c) - decides whether the character is a letter (a-z, A-Z) • intisalnum(int c) - digit or a letter? • intislower(int c) - lowercase digit? (a-z) • intisupper(int c) - uppercase digit? (A-Z) • intisxdigit(int c) - hex digit character? (0-9, a-f) • intisspace(int c) - white space character? • intiscntrl(int c) - control character? • intispunct(int c) - printing character other than space, letter, digit? • intisprint(int c) - printing character (including ' ')? • intisgraph(int c) - printing character other than ' ' (space)?
String I/O Recap • 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
Reading strings: get and getline • 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
Reading strings: get and getline • 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.
Reading strings: get and getline • Sample calls: char buffer[80]; cin>> buffer; // reads one word into buffer cin.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 • getlinewill extract and discard the delimiter character
get and getline examples 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 C string library: • 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
strlen • Takes one string argument, returns its length (not counting the null character) • Prototype: intstrlen(const char str[]); • Sample calls: char phrase[30] = "Hello, World"; cout<< strlen("Greetings, Earthling!"); // print 21 intlength = strlen(phrase); // stores 12
strcpy • 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"
strcmp • Takes two string arguments (both passed as const arrays), and returns an integer that indicates their lexicographic order • Prototype: • intstrcmp(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!
strcmp • 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)
strlen, strcpy, strcat, strcmp • 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
strncpy, strncat, strncmp • 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!