330 likes | 542 Views
Object-Oriented Programming in C++. Week 2 Pointer Variables Dynamic Memory. Introduction. Last lecture we looked at more aspects of functions constants, parameter passing and operator overloading introduced reference types This lecture we will introduce pointer variables
E N D
Object-Oriented Programming in C++ Week 2 Pointer Variables Dynamic Memory
Introduction Last lecture we • looked at more aspects of functions • constants, parameter passing and operator overloading • introduced reference types This lecture we will • introduce pointer variables • discuss dynamic memory • creating objects using new • deleting objects
Pointers in C++ • Variables have two values associated with them: • their content • and their address. • A pointer is a variable which stores the memory address of another variable. • Consider a program which includes the following declarations: char letter = 'c'; int num = 5, age = 25; note: the address values given are arbitrary examples
Addresses • The actual address is allocated by the system • not under programmer control • Unlike the contents, the address of a variable can not change during its lifetime. • However the address of a given variable may differ each time the program is run • The & operator gives the address of a variable. • Using the previous example: &letter is 1245020 &num is 1245012 &age is 1245000
Example • The following code displays a variable’s contents and its address: intnum = 32; cout << "num = " << num << " at address " << &num<< endl; • output: num = 32, address= 1245024 • We are not (usually) interested in knowing the absolute value of an address
Pointer variables • Pointer variables are used to store addresses of other variables. int num = 5; char letter = 'c'; int * pNum; char * pLetter; pNum= # pLetter = &letter; • pNum is a variable which will hold the address of an integer. • pLetter is a variable which will hold the address of a character • The declaration int* pNum means "*pNum is an integer, therefore pNum is a pointer to an integer".
Indirection operator • The * symbol is an operator in C called the indirection or dereference operator • it means the value at the location pointed to • The * operator is the inverse of the & operator • * pNum is the data at the memory location stored in pNum
Pointer variables int main() { int num = 10, y, *pNum; pNum = # y = *pNum; *pNum = 5; pNum = &y; } num 5 10 y 10 pNum 5024 5012
Pointer variables • we are not interested in the memory address stored by a pointer variable • just that it points to a particular chunk of memory #include <stdio.h> int main() { int num = 10, y, *pNum; pNum = # y = *pNum; *pNum = 5; pNum = &y; } num 5 10 y 10 pNum
Pointers vs. references • what's the difference between a pointer and a reference variable? • both don't hold actual data • they hold the address of where to find data • that address might have multiple references / pointers to it • the syntax of using a reference variable of a particular type is the same as the type itself
Pointers vs. references • reference variables must be initialised when they are declared • always point to the same address • although the data at that address can be changed • the address stored in a pointer can be accessed and changed • reference variables are • safer to use than pointers • have nicer syntax • are less flexible than pointers
Recall swap function – pass by reference void swap(int& a, int & b) { int temp = a; a = b; b = temp; } intmain() { int x =3, y = 5; swap(x, y); cout << "After swap function, x = " << x << " and y = " << y << endl; } After swap function, x = 5 and y = 3
Swap function – pass by pointer void swap(int * a, int * b); int main() { int x = 3; int y = 5; swap(&x, &y); cout << "After swap function, x = " << x << " and y = " << y << endl; } void swap(int * a, int * b) { int temp = *a; *a = *b; *b = temp; } After swap function, x = 5 and y = 3
Swap function – pass by pointer int main() { int x = 3; int y = 5; swap(&x, &y); } void swap(int * a, int * b) { int temp = *a; *a = *b; *b = temp; } x 3 5 3 y 5 b a temp 3
Array addresses • an array variable is actually a pointer variable intnum[ 5 ] = { 3, 1, 5, 2, 4 }; • the variable num is a pointer to an int • it stores the address of the first element of the array • same as &num[0] • num[0] gives the value of the first element • same as *num • num + 1 is the address of the second element • *(num + 1) is its value
Arrays and pointers • this means we can use pointers to access array elements • what does this do? int num[5 ] = { 3, 1, 5, 2, 4 }; int * pa; for ( pa = num; pa < (num + 5); pa++) cout << *pa << " "; Output: 3 1 5 2 4
Passing arrays to functions • in C and C++, arrays are passed to functions by reference • because the array variable is really a pointer • unlike in Java, the array does NOT carry any information about its size • need to also pass the array size to the function • a function to output an array: void outputArray(int a[], int size) { int i; for (i=0; i < size; i++) cout << a[i] << " "; } • to call the function: outputArray(num, 5);
Tutorial Question • Modify your Account class, using overloaded methods where appropriate, so that a cheque for a given amount can be withdrawn from the account • A sensible function prototype for this function: bool issueCheque(double amount, Cheque & cheque); • usage: Cheque myCheque(0, true); if (myAccount.issueCheque(30, myCheque)) { …
Returning a Cheque • what if we wanted to return a newly created cheque object? • instead of passing one in? Cheque issueCheque(double amount) { Cheque ch(amount, true); return ch; } • this could be used with a more natural syntax Cheque myCheque = myAccount.issueCheque(30); • this works – the Cheque is returned by value • a copy of ch is created and stored in myCheque
Returning a Cheque by reference • the only problem with the previous method is that it can be inefficient • we create a cheque • then when it is returned: • another cheque is created • the attributes of the original cheque are copied to it • the original cheque is destroyed • why not return a reference to the cheque created in the method instead?
Returning by reference Cheque & issueCheque(double amount) { Cheque ch(amount, true); return ch; } • usage: Cheque myCheque = myAccount.issueCheque(30); • warning C4172: returning address of local variable or temporary • heed the warning • chhas gone out of scope • the memory it occupied could soon be used for something else
Heap memory • it would nice to be able to create an object in a function that doesn't go out of scope when the function returns • so far all the objects we have constructed have been on the stack • heap memory is a separate area of memory that is managed separately from the stack • we can construct an object here and store a pointer to it on the stack • the object will exist until we explicitly delete it • even if all pointers to it go out of scope
Returning a pointer Cheque * Account::issueCheque(double amount) { Cheque * ch = new Cheque(amount, true); return ch; } • usage: Cheque * myCheque = myAccount.issueCheque(30); cout << myCheque->getAmount() << endl; delete myCheque;
Dynamically allocating objects • using the keyword new tells the Cheque constructor to create the Cheque object chon the heap • the constructor returns a pointer to the object • the address of where to find it • we then return the pointer from the issueChequefunction • returns a copy of the address of chto myCheque • we can access the myCheque members using the indirection operator -> myCheque->getAmount() • same effect as (*myCheque).getAmount() • but much nicer to use
Deallocating objects • now we don't need to worry about the Cheque object going out of scope • it will stay on the heap forever • or until the program terminates • if we are lucky • It is important to release memory once it is no longer required • Otherwise we have a memory leak • the memory is not available to create new objects
Deallocating objects • The C++ keyword deleteprovides this facility: delete myCheque; • delete calls the myCheque destructor • and releases the memory pointed to by myCheque • myCheque will still contain a pointer to the memory previously occupied by the Cheque object • at some point this memory might be allocated to another object
Memory leaks • it is easy to lose a reference to allocated memory • if the variable pointing to it is reassigned • when the variable goes out of scope • the allocated memory is NOT automatically released • if not released, it cannot be reused until the program terminates • in some systems, not even then • this is a MEMORY LEAK • a very bad thing
Memory management in Java and C# • in Java and C#, heap memory is allocated with the keyword new • when creating arrays and objects • the reference to these objects is like a pointer • Java does not rely on the programmer to manage memory allocation • instead, a garbage collector is provided • objects which are no longer reachable (for example, by going out of scope) remain in memory • at intervals (or when requested) the garbage collector runs • frees up this space • compacts the heap memory
Summary In this lecture we have: • introduced pointer variables • discussed dynamic memory • creating objects using new • deleting objects In the tutorial we will • practice using pointers and dynamic memory allocation