Reflections and Closing Comments

Thinking back on why I joined this class I recall that I felt my knowledge of C++ was shaky at best and most of the solutions I had put forward in previous classes had been sub par at best. Hoping to stifle this lack I charged forward scheduling my last and final semester around this class and Automata.

After having completed the course I can say that I was pleasantly surprised at how much I have learned and just how comfortable I am with C++. I now dare to say that I am approaching the same comfort level that I have with Java and PHP and my career which starts with a C++ stack seems less daunting now.

Often I have heard it expressed that teaching someone who doesn’t have any programming experience is much easier than teaching someone with prior experience in another programing language, and this had agreed with my thinking in the classes where I had undertaken learning C, Ruby, and LISP. With the teaching methods used though In this class I found that this was somewhat true still as evident by the memory management errors I encountered in my projects but I felt that the discussions in class(although delayed sufficiently) helped to ease this pain and prove that the idiom asserted above is not necessarily true given the right guidance.

I feel that the most valuable topics covered were the design patterns (implicit in the projects), the lectures on memory management, and best practices. While the lectures of lesser value to me were the lectures where we detailed the specifications of the projects.I would simply say that reading the documentation and querying peers has been critical in all of the internships I have held before and thus I was not anticipating spending two lectures orienting us at the anticipated solutions. I think Piazza, Stack Overflow, discussions with our partners, searches on Google, the project specs, and peer discussion should have been sufficient for this type of discussion/discernment. That being said this is an extremely minor nuance in the scope of things, and in my mind this class has been wildly successful in my eyes and has taught me a whole slew of things I would not have known otherwise.

handle classes

This entry comes a bit late as this final week of school has been keeping me on my toes and I was a bit delayed in publishing this to being with, hopefully any readers might forgive this tardiness and the berevity of this post.

The week of the 22 Apr was a fun-filled week full of last minute touches to the project as we began to go over the different approaches to pointers.

The first of which was a simple ‘has a’ relationship in which the handle class(henceforth ‘Handle1’) contained the pointer. This approach was nice because it helped to solve the problem of memory managment (from the perspective of the main program) and pushed this down into the Handle1 object and the containing class(as its destructor invoked the destructor of the handle). The drawbacks to this implementation was that to give access to the methods of the pointer we would either have to loosen the encapsulation of the pointer or define a common interface, we choose the common interface initially.

Handle2 was the second revision in which we loosened the encapsulation instead allowing pointer like operations with operator* and operator->. The good: this allowed us to not have to define the public interface, as such allowing for non-shared methods of the interface to be invoked (after verifying the type and typcasting). The bad: by giving up the pointer we lost control of the data and the user could swap it out and then we would have the potential to leak memory.

The third and final handle(Handle3) was our attempt at a copy on write pointer encapsulation. The objective being, since we often have a spare matrix in which many cells never get written it would be nice to not have to have a unique value of a null object for each non-used cell. The overhead is that we would need to track how many locations are using the same pointer by a separate count and as such increase the level of indirection so we could modify this count only once for all instances of the pointer and then only alter the one that we need to when written

In summation it would have been nice to use a copy on write pointer that implemented the common interface for the Life project, due to deadlines we went with a similar but not optimal approach in which we had the multiple levels of indirection and a common interface but initialized all of the pointers to null objects initially.

 

abstract classes and methods, Life, and function binding

This week in OOP we talked about approaches to the Life project, function bindings and how to use non-default bindings in C++/Java, and abstract methods and classes.

When we talk about function binding we are referring to the function that is invoked. The bindings that we have studied this past week are dynamic and static binding, with static binding the declared class of the object/variable is the function that is invoked, with dynamic binding the type of the value that is stored in the variable or the type of the object itself determines the function that is invoked. As you might have guessed by now java uses dynamic function binding by default and C++ uses static by default. However either can have the opposite binding enabled by keywords in Java this is accomplished by the use of private methods, static methods, final methods, or final classes. In C++ dynamic binding is enabled by using the keyword virtual on the parent method, this will cause a look up table to be built and used at runtime to determine the function to be invoked.

Java has the convention that either a class can be abstract and have no abstract methods or it must be abstract if any methods are abstract. As such disallowing instantiation of the class and all subclasses that choose to not implement the abstract methods. If a method is declared abstract then it can’t have an implementation as such you end up having to create protected helper methods if desired with different names. As you might imagine C++ is a slightly different story, you can have abstract(/pure virtual) methods but they may have implementations if you choose to provide them, however the methods must be implemented in function scope. The notion of an abstract class with no abstract methods does not exist in C++ however a pure virtual destructor would give you much the same result.

overloading, functions vs. methods, and rule of three

As we progress through the semester, and a fast approaching third test we find ourselves focusing on overloading, functions(symmetric operations) vs methods(asymmetric), and the rule of three.

    when overloading functions or methods in C++ there are several values that can be overridden. for functions we have: the types of the arguments and if the function is not an operator then we can also override on the number of arguments. Likewise with methods we can override on the type of the arguments and the const-ness of the method, if the method happens to not be an operator then we can also override on the number of arguments as well.

   In designing a class one of the questions that should be asked is whether the function could/should belong to the class which members it would need to take as an argument. To answer this question we can look at the constructor and if it is not marked explicit(as such allowing implicit conversion from the type of argument it accepts) then we should allow the function to remain outside of the scope of the class, this way we can have the implicit conversion for its arguments happen on either side. In addition to the last mentioned sided-ness argument one should also consider if the left hand side of the function needs to be of another class (for example the input/output streams) if this is the case then we again want to keep the function outside the scope of any class as we cant modify the stream class directly, so we let the globally scoped function do the work for us. Otherwise it might be appropriate to declare the function as a method instead.

 The rule of three was another topic covered and it quite simply says that if we need to override any of the destructor, copy constructor, or the copy assignment operations then we will likely need to override the other two of the ones mentioned above. To do this without repeating code we could create helper methods or we could define the copy constructor in terms of assignment or the assignment in terms of the copy constructor, the latter if probably more favorable as it leads to a three line solution involving calling swap.

testing, casting & implicit conversion, and methods vs functions

This past week in OOP we discussed how to test private and protected members of a class in C++ without having to change the implementation, casting and implicit conversion and how to turn it off in C++, and we looked at methods vs functions.

The first discussion was motivated by the project which was due on wednesday and the fact that it is nice to be able to test internal state but many of the solutions involved making the class a friend of the tester which required some work and portrayed an object model that was not entirely true, and furthermore might need maintenance in the future. The better solution was to use compiler directives to expose all of the members and as such the state, the code for which would look something like:

#define private public
#define protected public
#define class struct

#include "darwin.h" //the header file we wanted to include in the tester

With reference to casting we covered the fact that in C++ we can typecast an int to a double, which we can also do in Java, however we can also typecast a double to an int(with a loss of precision) which we can’t do in Java. Prof Downing also introduced that fact that the one arg constructor was an implicit conversion function from the type of the argument to the type of the object, in some cases though this is undesirable so by defining the constructor with the keyword ‘explicit’ we can force this implicit conversion to not happen, we then sacrifice the nice syntax of <Type> x = <value>; and we are forced into using the otherwise equivalent <Type> x(<value>); syntax. In C++ we can additionally define functions for other types to allow conversion of the user defined type to the other type, the syntax for which is as follows:

operator <some_type_here>(){...} //will always return a value of type <some_type_here>

In cases where these implicit conversions might cause problems the compiler will prefer the non-converted function.

Progressing through the list we come to the comparison of functions vs methods and when to use which. Functions belong to no class or rather they exist in the file/global scope whereas methods are bound to a class. Methods normally my first choice coming from Java but when deciding which to use one should ponder the following questions: “does the public interface provide all the access we need to perform the function?”, “do we need to define the operation with a different class on the left hand side(example: ostreams’  << and  istreams’ >> operators)?”, and “do we want the operation to be symetric?”. If you answered yes to any or all of the above then it might be worthwhile to define the code block as a function(using friend if necessary).

darwins game of life

As the semester is rapidly coming to a close I find myself still learning new lessons in OOP. Namely what the funky syntax is that I note on some constructors (initializer lists) and that I don’t fully understand the STL data structure.

When programing we like to have certain data be available to all of the instances of a class but have only one copy, in java this is easy, simply declare a static variable. In C++ the static variables exist but they operate in a slightly different way, namely they are ‘flavor’ or class specific so a subclass has a different static variable than the superclass. In C++ if you want the static instance to be nonchanging then you can declare it final and use the syntax <class>(<params>) : <name_of_static_final_var>(<value/variable>) { <other assignments and such>} this will have the net effect of assigning the variable/value to the static var. In java you can use static initializer blocks to do a rough equivalence to this.

In working with the STL libraries my partner and I ran into an interesting issue, we wanted the board to store the creatures and we wanted to be able to move the creatures easily, as well as not have a state explosion if the population of valid creatures was small(as it normally is with respect to the board). So our first approach was to make a vector for storage and use the pointers to the elements of the vector, however after much frustration we realized that push_back can cause the vector to grow and it might but will not always change the addresses of the elements. What was curious was that on a 32 bit machine all was well, on a 64 bit machine we failed some unit tests, however if we ran the program in valgrind everything worked fine. This goes to show that some compilers helping you out can cause a lot of frustration in the long run because they effectively mask the real problem at play.

Test Prep and Project reminiscing

This week we resumed where we had left off the weeks before we went on spring break wrapping up our discussion of different c++ array/pointer/vector equivalence notations. I found this discussion to have been rather enlightening but the high point was that vector provides near array performance speeds without the hassle and it provides handy initialization methods too.

After putting the finishing touches on the project I started to prepare my one page for the test, I found that the notation that confused me the most was the notion of pointers to functions as they didn’t follow the general pattern that I had associated with typedefs that being: typedef <type> <alias name>. Going into the test I felt fairly prepared and I hope the hours spent studying pay off. In reflecting on the test I can only say that I expected more diversity and I hope that all tests don’t follow this pattern as one missed class or one omission of a detail could prove cost up to half of the test.

templating vs pointers, array object vs primitive storage, and the glory of STL vectors in C++

This post shall be rather short in its nature as it is already past due and I am still trying to finish up my workload that I had on my “break”. Two weeks ago in OOP we took some time to observe the nuances of array initialization with a high point being demonstrated by quiz 20, namely showing that an array of type A instantiated with Bs would create the invocation pattern: B constructor, A constructor, A copy Constructor, B destructor and lastly when the Array exited the function scope of invocation A destructor would be called.We also looked at templating examples and pointers. The high point there being that templating allowed for more types of iterators than pointers, but that some iterators imposed restrictions on the order in which their functions were called, namely the read iterator, and the * and == operators. We also briefly discussed why the STL was nice, and the fact that vectors are similar but much faster than java ArrayLists. The high points being attempt to use STL or Boost to save some troubles and a reference to the fact that C++ vectors subscript([]) operator did not suffer from the linear time look-up like java’s List interface does, however there is some overhead on the .at operator.

 

Arrays and Pointers galore

This week in OOP, we spent most of our time iterating over the different types of pointers in C++ and motivating some examples, the discussion stems from the fact that there are 4 varients of pointers in c++ which can be summed up by the following code and comments:

int c;

int* b; // read/write, many locations

cont int* pc; // read only, many locations

int* const cp = &c; // read/write one location (same as doing int& cp = c;)

const int* const cpc = &c; // read only, one location

We took some time later on to discuss the fact that in java arrays are allocations of values only if the data members are primitive types and for all non-primitives the array holds references to the type, unlike in c++ where the arrays contain the values themselves and can either be stack allocated or heap allocated, unlike java where all stack values reside on the heap.We finished up the week by looking at pointers vs. arrays in c++ noting that pointers are rvalues where arrays seem to be lvalues.

Heap Emulation

This week in OOP we began focusing on type casting in C++ as well as allocation strategies and data storage strategies.

The type casting of C++ is a bit more verbose than it is in most other languages, the reason being is that often when typecasting data is truncated and latter we find that this data was needed, so since errors sometimes result C++ has taken to making typecasting easy to spot by giving it an overly verbose form. An example of this is the following:

int& view(char& c) const{
return *reinterpret_cast<int*>(&c);
}
 

The allocation and data storage strategies we reviewed were the following:

  • in C++ most systems interpret a char as 1 byte, as such it is a lightweight way to store general byte data.
  • to denote free space in a heap we can create sentinel values as int values(aka: 4 bytes), often these values might get overwritten accidentally, so to provide some minor error checking store two copies of each sentinel required.
  • to denote active/taken blocks use a negative value to distinguish between free blocks(positive values)

By using this information as well as some unit tests developed on the native heap manager we hope to achieve a similar heap manager that allows us more granular control of the data it has allocated, this is stricly for educational purposes of course.