CS62 - Spring 2011 - Lecture 34

  • opening file streams with a string parameter
       - ofstream fout(filename.c_str());
       - ifstream fin(filenmae.c_str());

  • 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 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 constructed 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

  • defining your own copy constructor and operator= methods
       - sometimes (often when your dealing with pointers, and specifically almost any time you have "new" in your code you need to override the default copy constructor and = operator
       - the copy constructor
          MyClass(const MyClass &rhs)

          - a constructor that takes an object of that type (passed by constant reference) and makes a copy of that variable
       - the operator=
          const MyClass& operator=(const MyClass& rhs)

          - has the special name "operator="
             - in fact, to override any operator, it's a similar format
          - takes as a parameter an object of that type (passed by reference)
          - what is the return type?
             - returns a MyClass object. Which makes sense, since we're assigning it to something that is of type MyClass
             - return-by constant reference
                - same basic idea as parameter passing
                - Java is always return-by value, that is the value being returned is copied
                - C++ has:
                   - return-by reference
                   - return-by constant reference
                - why?
                   - like call-by reference, it allows us to return objects without copying them
                - why the const?
                

  • look at intcellfull class definition in big3.cpp code
       - as always, we declare the function definitions in the header file
       - copy constructor is generally easier
          - we're given an IntCellFull object and we just need to copy it
          - in this case, that just means making sure we point "value" at a new heap int location
             - rhs.value gives us the value
             - since it's a pointer, we need to dereference it with *
       - operator=
          - remember, the operator= is not a constructor. We have two pre-existing classes and we're just trying to make the current one equivalent (or equal) to the one passed in (on the right hand side of the = sign)
          - this
             - just like Java, "this" references the current object
             - in C++, this means that it is a pointer!
                IntCellFull* this
                 or in general   
                MyClass* this
             - need to use -> or * since it's a pointer to access member variables and member functions
          - the if statement is almost always there in the operator= function. What is it checking?
             - making sure we're not trying to copy ourselves
             - a = a;
          - what's pointed to by value, gets what it pointed to by the value from rhs (what we're trying to copy)
          - return *this
             - also very common

  • destructors
       - Anything wrong with doing the following in Java?
          Vector v = new Vector();
          v = new Vector();

          - nope
          - functionally everything works fine. v now points to a new Vector object on the heap
          - eventually, the garbage collector will notice that the original Vector object is free and reclaim it
             - when can an object be garbage collected?
                - when there are no references remaining to that object
       - what about in C++?
          vector<int>* v = new vector<int>;
          v = new vector<int>;

          - functionally, everything works fine
          - the problem, is that C++ is not garbage collected
          - we now have a chunk of memory that we cannot access, but also cannot reuse
       - in C++ we need to explicitly tell the memory manager that we're done with that memory
       - the "delete" command does this:
          vector<int>* v = new vector<int>;
          delete v;
          v = new vector<int>;
       
          - we told the memory manager that we don't need the memory allocated to the first object
       - objects can be very complicated. how does the memory manager know what memory to free up?
       - the destructor: one final method that the compiler provides a default implementation for:
          ~MyClass()

          (opposite of the constructor)
       - the destructor is called when an object is no longer needed
          - when the programmer explicitly calls delete on an object
          - Any other times when a variable might not be needed anymore?
             - when a variable's scope is disappearing
                - for example, a local variable when we return from a method
       - what do you think the default behavior for the destructor is?
          - recursively calls delete on all of the member variables
             - doesn't matter for built-in types