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