CS62 - Spring 2011 - Lecture 33

  • Admin
       - Computer science pre-registration pizza, Thursday at 5pm in Edmunds lobby
       - If you're interested in TAing next semester, I forwarded the application (Due: April 24th)
       - Prof. Kauchak's Thursday office hours move: 10-11am => 1-3pm
       - will update reading for C++ book
       - I will post practice problems (with solutions) on the same page as the lectures. You will be responsible for any of the problems for prior classes. I WILL give a quiz on one of these at some point. The quiz will consist of exactly one of the problems.

  • Pointers revisited
       - 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
          - just like for built-in type, the = operator copies the memory address for pointers
       - 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 swapAttempt?
          - 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 roughly 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 an '&' 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 backwards compatible
          - they wanted to avoid introducing new keywords and then having to modify existing C code

  • Look at Simple.java in big3.cpp code
       - what does this print out? why?
          - 0
          - if an object is used in the context of a String, it's toString method is implicitly called and that value is used.
          - this always works, why?
             - the Object class has a toString method defined for it
       - why does this compile even though we never specified a constructor?
          - the Java compiler implicitly defines the default constructor for you if you don't provide one!
             - it would be the same as putting in a constructor with no code in it

  • Look at intcellexamples.cpp in big3.cpp code
       - Take a quick look at intcell.h to remind ourselves of the IntCell class (it's like a stripped down version of Integer in Java)
       - We've created 4 different IntCell variables
          - 'a' was created with the constructor
          - 'b' was created by assigning it to 'a' in the constructor statement
          - 'c' was created by passing 'a' as a parameter to the constructor
          - 'd' was created with the constructor and then assigned later on to 'a'
       - Where is the constructor used to create 'c' come from?
          - this is called the "copy constructor" and is implicitly provided by the compiler if we don't explicitly define one
       - What will be the output?
          - interestingly enough, 1, 2, 3, 4!
       - What does this imply?
          - a, b, c, and d all are separate objects.
       - Is this surprising?
          - the output should not be that surprising
             - we've not declared any of these variables as pointers, so we know they're independent objects on the stack
          - the surprising thing is that this somehow works as we expect it should work

  • The C++ compiler provides two default functions for you: copy constructor and the = operator
       - copy constructor
          - the copy constructor is called for both 'b' and 'c'
             - 'c' is not surprising since it looks like a constructor
             - 'b' is just another way of writing it, but the copy constructor is still called
          - by default, the copy constructor
             - for objects: apply copy constructor calls to each of the member variables
             - for built-in types (e.g. int, double and pointers!): simple assignment is done, that is, copy the value
       - = operator
          - called when the = operator is used and the left hand side is NOT a new variable definition (that is, when we have two already constructed objects)
             - d = a
          - by default, the = operator
             - for object: apply the = operator on each of the member variables
             - for built-in type: again, simple assignment
       - default behavior
          - is intuitive: to copy an object, you simply copy all of the member variables
          - works in many situations (like our IntCell example above)

  • Look at IntCell2 class definition in big3.cpp code
       - What has changed from IntCell?
          - instead of storing the value as an int, we're storying it as int* and keeping the value on the heap
             - again, do as I say, not as I do :)
             - there are rarely situations when you would actually need/want an int pointer or storing an int on the heap, but it makes the examples simpler
          - another difference with C++, we can store built-in types on the heap! (that is, without wrapping them in an object)
          - What class does this look like in Java?
             - similar to the Integer class
       - What would happen if in intcellexamples.cpp we changed the variables to be of type IntCell rather than IntCell2?
          - we now get 4, 4, 4, 4 as a result. Why?
          - a, b, c and d still are 4 separate objects on the call-stack
          - the problem is that their private member "value" all point to the same thing. why?
             - when 'b' and 'c' are constucted the default copy constructor is called
                 - the default behavior for pointers is to copy the value over
                - this is called a "shallow" copy of the data structure
             - similarly, when we set 'd = c' the default = operator is a shallow copy