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)