CS62 - Spring 2010 - Lecture 20
look at
first.cpp code
- 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 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
- for example, an int takes up exactly 32 bits, a double 64 bits, etc.
- the value of the int is exactly written in memory
- show a picture
- what will be printed out?
int x = 10;
int y = x;
y = y + 10;
System.out.println(x)
- the built-in types are NOT references
- the = operator copies the value
- everything else is a reference to an object
- a reference is another variable, and it too goes on the stack
ArrayList<Integer> x = new ArrayList<Integer>();
ArrayList<Integer> y = x;
y = null;
System.out.println(x)
- similar to above, the assignment operator simply copies the value of x to y, which is a reference to an ArrayList
- we can change the value of y and it won't affect x
- what do you think a reference actually is, i.e. how is represented in memory?
- a reference is just a memory address
- traditionally, a memory address was represented as 32 bits
- so an object variable uses up 32 bits
- how much memory can you address with 32 bits?
- 2^32 different numbers = ~4 billion numbers
- each number represents a byte
- 4 gigabytes
- my mac has 4 GB of ram, do you think this is a coincidence?
- again, traditionally, memory was accessible by 32 bits
- most processors operated in 32 bits
- what is a 64 bit processor?
- it's a processor that operates on 64 bits
- what is the advantage?
- much bigger memory space
- disadvantages?
- if we only need 1G of memory, we're going to waste space because references now take up twice as much space
- you will hear people talk about 32 JVM vs. 64 bit JVM
- since java is a "virtual machine" it can have either 32 bit or 64 bit addresses
- heap
- anytime you say new, you get a new object on the heap, and you end up with a reference
ArrayList<Integer> x = new ArrayList<Integer>();
ArrayList<Integer> y = x;
y.add(10);
System.out.println(x)
- now, x will be changed because they still reference the same object. "add" is a mutator method
- what do you thing the "new" operator returns?
- the "new" operator, returns the address in where the newly created object resides
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 define 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
what would happen if we moved main to the top in our first.cpp example?
- doesn't compile
"first.cpp: In function Ôint main()Õ:
first.cpp:7: error: ÔsimpleVersionÕ was not declared in this scope"
- ?
- unfortunately, the scope of a method (i.e. the things that it has access to are only things that have been declared before it.
- in C++, the compiler starts at the top of the file and works its way down. If it hasn't seen a method before, it's going to complain.
- in this case, we can just move the main method down.
- mutual recursion example: what does these methods do?
bool a(int number){
if( number == 0 ){
return true;
}else{
return b(number-1)
}
}
bool b(int number){
if( number == 0 ){
return false;
}else{
return a(number-1);
}
}
- let's start with some small numbers for the "a" method
- 0 -> true
- 1 -> false
- 2 -> the same as 0
- 3 -> the same as 1
- 4 -> the same as 2, which is the same as 0
- return whether or not the number is even
- induction again :)
- "b" is just the opposite method, whether it is odd
- these methods are mutually recursive, in that they call eachother recursively
- we can tell the compiler that we're going to define a method at some point, by just writing the method declaration, followed by a semi-colon (similar to an interface or an abstract method)
- in the above example we could do:
bool b(int number);
to declare the header for the method so that the compiler knows that it's coming
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
- 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 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
- everytime 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 in a bit)
- 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:
"intcell2.cpp: In member function Ôvoid IntCell::setValue(int) constÕ:
intcell2.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)
look at
bankaccount.cpp code
- bankaccount.h
- header file with a few more declarations
- none of the examples here have private methods, but you can also declare those in the header file
- bankaccount.cpp
- method definitions
- again, notice that the private variables only occur in the header file
- bankaccounttest.cpp
- notice we put the main method in another class
- straightforward use of the class
- notice again the calls to the constructor
- compiling
- we now have multiple class files that we want to compile
- can't just do it with one line
- C++ has three main steps in compiling
- preprocessor
- compile to machine code
- link all of the machine code files into one project
- preprocessing happens automatically when compiling
- need to compile each .cpp file individually
g++ -c bankaccount.cpp
g++ -c bankaccountest.cpp
- the lines above could be done in either order, why?
g++ -o bank bankaccount.o bankaccountest.o
- link all of the files together and make a binary
- binary is called bank
- you can get "link" errors if, for example, you haven't defined a method that you said you would
- why don't we have to compile the header files?
- they get #included and implicitly compiled. You can get compiler errors in header files
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)