CS62 - Spring 2010 - Lecture 21

  • assignment 10 things
       - commenting
       - pair and map classes (read the specifications!)
          map<int, int> m; // sets up a new map (hashtable) with an int key and an int value
          m[0] = 4; // sets key 0 to have value 4
          m[17] = 7; // sets key 17 to have value 7
       - debugging when compiling at the command-line
          - if you get compiler errors, go to the top (i.e. the first error) and fix that one then recompile
          - "segmentation fault"
             - this is C++'s nice way of telling you you screwed up
             - equivalent to a run-time error
          - use print statements (i.e. cout)

  • pointers
       - in Java, we have two high-level types of variables
          - built-in types
          - references to objects
          - (and really arrays as a third type)
       - in C++, we have three. We've seen two. What are they?
          - built-in types
          - object variables on the stack
          - (and again, we also have arrays)
       - the third general type are "pointers" or "pointer variables" (similar to references)
          - Like Java references
             - a pointer is a variable that stores a memory address
             - a pointer can only point at an object of that type (more or less)
          - Unlike Java references
             - not all object variables are pointer variables (as we saw last time)
                vector<int> v; // is not a pointer/reference
             - to distinguish, we put a '*' after the type indicates that variable is a pointer variable rather than a traditional variable
             - unlike Java, we can have pointers to built-in types as well as objects
          - A few examples:
             vector<int>* vPtr;
             string* sPtr;
             int* intPtr;
             IntCell* cellPtr;
       - Using pointers
          - Like Java, we can use the "new" command to create a new object
             - where do you think this is stored? On the heap!

             vector<int>* vPtr = new vector<int>; //

             the "new" command returns the address of the newly created object

             - notice that:
                vector<int> v = new vector<int>; // will NOT work
             - why?
                - different types
                   - the new command will return a vector<int> pointer
                   - you're trying to assign it to a vector<int> variable

                   "conversion from Ôstd::vector<int, std::allocator<int> >*Õ to non-scalar type Ôstd::vector<int, std::allocator<int> >Õ requested"
          - A pointer is just a memory address (the type is for type checking by the compiler)
          - Because it is just a memory address, you use them slightly differently than a normal variable. To access the data that the variable "points" to, you need to "dereference" the pointer.
          - "dereferencing a pointer": Given a pointer: vector<int>* vPtr;
             - you can dereference the pointer again using the '*' operator and then use methods using '.':
                (*vPtr).push_back();
                (*vPtr).size();
             - you can use the '->' operator (which is generally preferred):
                vPtr->push_back();
                vPtr->size()
             - look at pointers.cpp code
                - what does objectPointer do?
                   - creates a new int vector
                      - where is it created? on the heap
                      - for the default constructor, you can either include or not include the parenthesis
                   - adds the numbers 0 through 9 into the vector (note the use of (*v) to dereference the pointer
                   - prints out the numbers
                - what will be printed out?
                   - remember, a pointer is just an address. The cout statements printing v, will just print the memory address where the object is stored
                   - will this change during the method call?
                      - no, we never assign anything different to it so it won't change
                   - all the address print statements will print the same address
                - what does objectPointersBetterApproach do?
                   - same thing, however, we're using the "->" operator rather than dereferencing the pointer
                   - in general, a better approach
          - There are two ways to get a memory address (i.e. the value for a pointer)
             - using "new", that is an address on the heap (like we've seen)
             - you can get the address of any variable using the '&', "address-of" operator
                vector<int> v;
                vector<int>* vPtr = &v;

                int x = 10;
                int* xPtr = &x;
          - look at pointers.cpp code
             - what will be printed out by the objectPointers2 method?
                - 10, 20
                - vPtr is just a pointer to vec, so the push_back calls push values onto vec
                - where is vec stored?
                   - on the call-stack! (remember this...)
                      - if you play with these programs you'll actually notice a difference
                      - when we printed out the address above on the heap it was at:
                         0x100150
                      - here, when we print out the address on the stack it's at:
                         0xbffff920
                      - which are not very close to each other!
             - what will be printed out by the address method?
                - the address of x, again on the call-stack
                - 10, which is just the value of x
                - notice that even though x is a built-in type, we can point to it
                   - when we dereference a built-in pointer, we just get the value (just like for object pointers we got the object)
                - in general, most of the time, you don't use pointers to built-in types, just objects
             - what will be printed out by the changingValue method?
                - The address doesn't change
                - print out 11
             - what will be printed out by the precedence method?
                - be careful with the precedence of '*'
                - in general, you should just point parenthesis when dereferencing a pointer
                - here, we end up incrementing the memory address of the pointer
                - we don't change the value of x
                - and now xPtr points to a random location in memory
             - what does twoPointers print out?
                - 20
                - both xPtr and x2Ptr point to the same location in memory
       - NULL
          - like Java, we can talk about a pointer being null
          - in C++, it's capital NULL
             - you need to #include <cstdlib> to use NULL (I know, it's annoying)
          - unlike in Java, pointers do are not initialized to NULL, so be careful
       - pointers can be tricky. Think carefully about what you're doing!

  • parameter passing: look at parameters.cpp code
       - what will happen if we call test1?
          - x and y will remain the same
       - why?
          - Java and C++ use what's called "call-by-value" parameter passing
          - when a method is called, the value of the actual parameter is copied to the value of the formal parameters
             - x and y outside the swapAttempt method reference different variables than those inside swapAttempt
       - could we do what we want to do using pointers?
          - look at swapWithPointers method
             - rather than passing ints, we can pass int pointers (i.e. addresses to ints)
             - we can then use '*' to get at the values stored at those memory address and swap the actual values
          - how should we call swapWithPointers?
             - need to provide an address:
                swapWithPointers(&x, &y)
          - notice that this is still call by value
             - the value of &x (i.e. the memory address of x) is copied to the variable int* x
       - C++ also provides another parameter passing mechanism called "call-by-reference"
          - in call by reference, the formal parameters are the same thing as the actual parameters (there is no copying)
          - they "reference" the same things/memory addresses
          - in C++, you can denote that a parameter for a method should be passed as call-by-reference using the '&'
             - NOTE: this is not the same as the unary operator for getting the address of a variable
       - look at swapWithReference method
          - notice that we use the variables normally (in particular, they are NOT pointers)
          - however, because we passed them by reference, the x and y passed in are the SAME as the x and y used in the method. If we swap x and y in the method, we swap the parameters passed in as well
       - look at binarySearch method and the testBinarySearch method
          - binarySearch is just the C++ version of iterative binary search like we saw in the first few weeks of class
          - testBinarySearch just generates some random numbers between 0 and 9 (inclusive), sorts them and then tries to find 7
             - srand seeds the random number generator (here, based on the current time)
             - sort, sorts the vector
          - as we increase the "size" parameter of testBinarySearch, how would you expect to see the running times grow, that is, what is the run-time of the binarySearch method?
             - we saw before that binarySearch is O(log n)
             - however, the running times would actually scale more linearly
             - why? what type of parameter passing mechanism are we using?
                - the problem is that it's call by value, which means we have to copy the value over, which is linear
                - why doesn't Java have this problem?
                   - all non-built-in variables are references, so while we do copy the values, it's only the value of the reference
          - solutions?
             - we could use pointers, but we'd have to modify the code
             - an easier way is just to use call-by-reference
                - just change the parameter passing mechanism by adding a '&' in the formal parameters
                - any downside?
                   - what would happen if in binary search somebody typed:
                      nums = NULL;
                   - when we returned, we'd find our vector variable was now NULL :( Remember, when using call by reference the actual and formal parameters are one and the same
       - call-by-constant reference
          - in C++, the keyword "const" refers to something as being constant (like "final" in Java)
          - something that is declared const cannot be changed in the code
          - where have we seen const used before?
             - in declaring a method to be an accessor method
          - Another way const is used is to define call-by-constant reference, add "const" before the variable type and also include the '&' for call-by reference
             int binarySearch(const vector<int>& nums, int findeMe
          
             - you then cannot modify the variable nums (either directly or via mutator methods)
                - another good reason to use const appropriately when declaring accessor and mutator methods
       - as an aside, if you haven't noticed, C++ is famous for reusing different keywords in many different contexts (though usually related). Why did they do this?
          - C++, was built on top of C and was tried to be made to be backwords compatible
          - they wanted to avoid introducing new keywords and then having to modify existing C code