Tag Archives: OOP

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.

 

Advertisements

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.

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.

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.