ECS 110: Data Structures and Programming Discussion Section Notes -- Week 1 Ted Krovetz (krovetz@cs.ucdavis.edu) ======================================== This week we will cover in section - What is Homework 1? - Parameter Passing in C++ - Function Overloading - Shallow Class Copying - Copy Constructor - '=' Operator Overloading ======================================== 1. WHAT IS HOMEWORK 1? ---------------------- In propositional logic you write formulas like ((P or Q) and not Q) Assigning values of TRUE or FALSE to each of the variables listed in the formula forces the formula itself to have a truth value. Above, gives us this truth table P Q ((P or Q) and not Q) T T F T F T F T F F F F Because there is an assignment for P and Q that makes the entire formula evaluate to TRUE, the formula is said to be satisfiable. 3CNF (3-Conjunctive Normal Form) is simply a stipulation that formulas be written as the conjunct of clauses which are themselves disjuncts of exactly 3 different variables. For example (V1 or V2 or (not V3)) and (V4 or V5 or V6) and ... and ((not Vx) or Vy or Vz) Note that variables can be repeated in different clauses and that variables may be complemented. A straight-forward approach to solving the question as to whether a formula is satisfiable would be to enumerate all possible truth assignments and answer "YES, it is satisfiable" if ever an assignment was found that satisfied the formula. And, if all assignments are tried and none are found to be satisfiable, then the answer would be "NO, it is not satisfiable". This brute force approach works fine for small formulas. However, if our formula had 64 variables, then there would be 2^64 potential assignments to try. If we checked 1,000,000 assignments per second, it would still take 600,000 years to exhaust all the possibilities. Rather than do that, you are given a heuristic that goes something like this. To solve one instance do the following repeatedly until solved. - Select a random truth assignment. It is unlikely that this truth assignment satisfies the formula. - Select at random one of the unsatisfied clauses. Flipping any of its variable's assignment will cause it's clause to become satisfied. Flipping will also probably cause some other clauses to change satisfaction status. - Flip whichever variable results in the most satisfied clauses. - If doing this for some number of iterations does not solve the formula, start over with a new random truth assignment. We will give you a data file similar in format to hw0, with multiple runs of the program. Each run is more difficult than the previous. Your program should execute until 30 CPU seconds have expired. 2. PARAMETER PASSING IN C++ --------------------------- There are two useful new additions to C's parameter passing; reference parameters and const parameters. In C all parameters are passed by value, meaning that a local copy is made for each parameter passed. This forces the use of passing pointers if the called function is to alter any data permanently or if the object being passed is large. In C++ you can append the & character to a parameter's type, indicating that the object should be passed by reference. Much like pascal's var parameters, changes to the parameter variable alter the originally passed object. Example: void swap(int& i, int& j); { int k = i; i = j; j = k; } Also, parameters can be declared const, indicating to the compiler that the variable will not be altered within the scope of the function. This is good practice because it helps the compiler show you accidental errors and communicates important information to the calling function. Example: void print(const int i) { i = 20; // ERROR! Assignment not allows } Const and & can be used together in the declaration of parameters to save the overhead of copying large objects but also ensure no alteration of the data. 3. FUNCTION OVERLOADING ----------------------- In C++ we can declare multiple functions with the same name as long as they take different types of parameters. This is useful when you have some similar action you wish to take, but on different types. For example: double min(double a, double b) { if (a < b) return a; else return b; } int min(int a, int b) { if (a < b) return a; else return b; } 4. SHALLOW CLASS COPYING ------------------------ C++ uses a "shallow copy" semantics when copying instances of the class. This means that whenever a copy of a class is needed, the default behavior is to copy each data member individually. This is often okay, but not if pointers are involved. For example, last week we wrote a class which was declared like this class CharStack { public: Stack(); ~Stack(); void Push(char); void Pop(); int Top(); int Empty(); private: int* storage; int length; }; If we had two CharStack variables CharStack cs1, cs2; Then our memory organization would look something like this: [draw picture of two variables pointing to two different storage arrays] If we then did this: cs1 = cs2; The data members would be copied resulting in this: [alter picture to show both variables pointing to the same memory] Also, when passing a class variable to a function, C and C++ use pass by value parameter passing which means a local copy of the variable is made. Once again, the copy will be a shallow one and bad things could happen. C++ gives us some mechanisms to avoid these problems. 5. COPY CONSTRUCTOR ------------------- First is the copy constructor. Whenever a copy of a variable is needed, and the destination variable did not already exist, C++ calls on the copy constructor to correctly initialize the new instance. The semantics of call-by-value parameter passing require that a local copy of the argument type be created and initialized from the value of the expression passed as the actual argument. This requires the copy constructor. Because C++ lets us overload functions (so long as the signatures are different), we can have more than one constructor. One special overloaded constructor is the copy constructor. Its syntax is that of a normal constructor, but taking one constant reference argument of the same type as the class. For example, CharStack(const CharStack &cs); In the copy constructor you would do the normal initializations followed by whatever is needed to make the new CharStack equivalent to the old on(cs). CharStack::CharStack(const CharStack &cs) { storage = new int[max_length]; length = cs.length; for (int i = 0; i < length; i++) storage[i] = cs.get_elem(i); } Note that I could not have made the last line storage[i] = cs.storage[i]; because storage is private, and so in the passed in variable is not accessible. So, a new public member function is needed to access arbitrary items in the stack. [Note: This muddies the interface. There are ways around it using the keyword 'friend', but we won't worry about that today.'] 6. '=' OPERATOR OVERLOADING --------------------------- Just as we can overload functions, C++ allows us to overload operators. To avoid the problem of shallow copying pointers we can overload the assignment operator, '='. During assignment, it can be assumed that the target has already been initialized, and all we really want to do is alter the target so that it is equivalent to the source variable. For example, void CharStack::operator=(const CharStack& cs) { length = cs.length; for (int i = 0; i < length; i++) storage[i] = cs.get_elem(i); } C and C++ allows multiple assignment, but if we wanted to use this new assignment multiply, we'd have to rewrite it like CharStack& CharStack::operator=(const CharStack& cs) { length = cs.length; for (int i = 0; i < length; i++) storage[i] = cs.get_elem(i); return (*this); } 'this' is a predefined pointer in C++ which points to the object you are currently working on.