CS62 - Spring 2011 - Lecture 30
What would the following print out in Java?
- Case 1:
int x = 10;
int y = x;
y = y + 10;
System.out.println(x)
- Case 2:
ArrayList<Integer> x = new ArrayList<Integer>();
ArrayList<Integer> y = x;
y = null;
System.out.println(x)
- Case 3:
ArrayList<Integer> x = new ArrayList<Integer>();
ArrayList<Integer> y = x;
y.add(10);
System.out.println(x);
- Why do these behave differently?
memory in Java
- stack (run-time stack)
- ALL variables go on the stack
- eight built-in types
- whenever you create these, they reside directly on the stack
- the built-in types are NOT references
- the = operator copies the value
- in Case 1 above, y gets copy of x, but they are distinct variables
- everything else is a reference to an object
- a reference is another variable, and it too goes on the stack
- similar to above, the assignment operator simply copies the value of x to y, e.g. a reference to an ArrayList
- we can change the value of y and it won't affect x
- in Case 2 above, changing the value of y, does not change what is referenced by y (or x)
- heap
- anytime you say new, you get a new object on the heap, and you end up with a reference
- in Case 3 above, x and y reference the same object and the add method changes the object referenced by y (and x)
look at
first.cpp code
- what does max do?
- finds the maximum value in the vector
- looks almost identical to what we would write in Java
- rather than using get and set, we access elements via [], like in an array
- what does simpleVersion() do?
- creates a new vector
- how many elements in it initially?
- 0, it uses the default constructor
- adds 1, 2 and 3 to the vector
- calls max, to get the maximum value
- prints out the maximum value to the console
- in this case, 3
- what does moreInteresting() do?
- again, creates a new vector
- creates a second vector nums2
- sets the first entry in nums to be 15 (rather than 1)
- calls max on nums2
- what do you think is output?
- two options:
- 3
- 15
- what would be output in Java?
- 15
- 3 is actually output
- what does this mean? why do you think this happens?
- nums and nums2 are different objects
memory in C++
- has both a heap and a run-time stack
- like Java, any variable you construct without using the keywords "new" is created on the run-time stack
- also like Java, a variable that isn't assigned to something on the heap is NOT a reference
- where is "vector<int> nums" created?
- it's a vector that is created on the run-time stack!
- this still doesn't fully explain our example?
- we know that "vector<int> nums2" is not a reference
- it doesn't reference anything on the heap
- there's a special notation for references in C++
- copy constructor
- all objects have a copy constructor (either explicitly defined or implicitly)
- the copy constructor takes an object of that type and creates a new copy
- nums2 ends up as a new vector on the stack that is a copy of nums
- therefore, changes to nums, do not affect nums2 and vice versa
the C++ compiler
- in
first.cpp code
, what is:
void simpleVersion();
- the C++ compiler starts at the top of the file and works its way down
- it only knows about the methods that it's seen so far
- therefore, if you want to use a method that you define later on, you need to include the method header declaration before using it
writing a class
- look at intcell_basic.cpp class in
intcell.cpp code
- what does this class do?
- a shell class to hold an int
- similar to the Integer class in Java
- what are some of the differences you notice between Java and C++?
- there is a "public" section and a "private" section where public and private things are defined
- you don't use the keywords public/private individually on a method
- semi-colon at the end of the class definition, it's just a statement
- you can put multiple class definitions in a file
header files
- As we noted above, you need to declare methods before you use them
- when declaring a class, the common thing to do in C++ is to split the class definition into two parts:
- a "header" file (with a ".h") extension with the definition of the class, the methods headers and the variables
- an "implementation" file (with a ".cpp") that has the actual definition of the methods
- look at intcell_take1.h in
intcell.cpp code
- the header file looks just like our class definition, except none of the methods have bodies
- look at intcell.cpp in
intcell.cpp code
- first line includes the header file
- what does this do?
- remember a #include simply copies the contents of the file
- now we know we have those methods available
- notice that it is only method definitions and nothing else in the file. All of the variable declarations, etc. are in the header file
- what does "IntCell::" mean in front of all the methods and why is it there?
- defining a class, defines a new namespace
- to differentiate between a global method getValue() and the one for the class IntCell, we need to use "IntCel::"
- notice that we don't have any public/private annotation on the methods in the .cpp file, only in the header file
- this is the approach we're going to use for this class
- what benefit does this have?
- what we've talked about a lot in the class is a separation of definition and implementation
- makes the code clean, by avoiding having to put in method header declarations in the code
- faster to compile! why?
- what does #include do?
- copies the file
- every time we recompile, we'll have to recompile all of the code we imported
- allows us to share compiled code, without sharing the actual code (more on this later)
- compiling with header files
- you don't need to explicitly compile header files. Why?
- they're included with #include
const
- what are accessor and mutator methods?
- an accessor method does not modify the class data
- a mutator method changes the data
- in C++, the keyword "const" after a method declaration states that that method is an accessor method
- it's a contract between the programmer and the compiler that we won't be modifying the object
- what methods could be const in IntCell?
- getValue
- make the change and recompile
- notice that you need to change it in both the header and the cpp file. It is part of the method declaration
- if we try and add const to setValue(), we get a compiler error:
"intcell.cpp: In member function Ôvoid IntCell::setValue(int) constÕ:
intcell.cpp:12: error: assignment of data-member ÔIntCell::valueÕ in read-only structure"
- again, we will do this in the class
- what benefit does this have?
- another way of communicating with other programmers and the compiler
- forces you to think about your code :)
- allows the compiler to make optimizations and makes your code faster
#ifndef, #endif and #define
- what happens if we try to declare a method twice (or just the method header twice)?
"intcell.h:4: error: ÔIntCell::IntCell(int)Õ and ÔIntCell::IntCell(int)Õ cannot be overloaded"
- compiler thinks that you're trying to "overload" a method
- what is an overloaded method?
- can't declare the same method twice
- what if we said:
#include "intcell.h"
#include "intcell.h"
in a file?
- we'd get similar errors for all of the methods
- the example above is contrived, but this can happen easily, when?
b.h:
#include "a.h"
c.h:
#include "a.h"
#include "b.h"
- to avoid this, you'd like to be able to state that you only want the header file to be copied once for any compilation
- we do this with preprocessing statements
- look at intcell.h in
intcell.cpp code
- #ifndef
- "if not defined"
- #define
- define the variable
- #endif
- end the if statement (don't forget this)
admin
- CS senior presentations Thursday and Friday afternoon
- guest lecturer on Friday in class
- send videos
assignment 10 things
- commenting
- pair and map classes (read the specifications!)
- pair
- used to store two items (a first and a second)
- map
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
- to ask if something exists, use the count method (do NOT try and use [] to see if something exists)
- destructors
- in C++ we have constructors AND destructors
- a destructor is called when an object is getting deleted/removed and is a chance for an object to clean up anything it needs to
- like the constructor, the destructor has the same name as the class, but is preceded by a '~', for example:
~IntCell();
would be the define the destructor for the IntCell class and then we could define it in the .cpp file as:
IntCell::~IntCell(){
}
- for this assignment, we'll include the destructor just to get you in the habit of it, however, you won't need to put any code in it
- 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 or cerr)
differences between ArrayList and vector classes
- what would the following print out?
vector<int> nums(10);
nums.push_back(1)
for( int i = 0; i < nums.size(); i++ ){
cout << nums[i] << endl;
}
- be careful about making assumptions about the behavior of classes
- the one parameter constructor for vector in C++ creates a vector which contains 10 elements in it already
- after calling the constructor, those elements exist and we can access the elements
vector<int> nums(10);
nums[8] = 10;
- what would happen if we did this in Java?
- index out of bounds exception
- what do you think will happen if we do:
vector<int> nums;
nums[8] = 10;
- run-time error (aka segmentation fault), just like Java, we cannot access elements in the vector that have not been instantiated (either via a push_back or via the constructor)