GCO4020/CSC428 - Advanced Object Oriented Techniques In C++
Week 5

 

Topic 9: Envelopes and Letters

Solutions to Exercises


Synopsis


Solution to Exercise 1

The Draft C++ Standard (TDCS, § 9.5) requires that members of unions be either in-built types or data structures which have:

The STL¤ string class fails all four of these requirements and so cannot be a member of a union. Hence a separate member is needed to store string values.

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 2

Other suitable members of class Value include:

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 3

Unlike preincrement, addassign and assign, the convertToBool function cannot change the value or the type of value that its letter stores (under any reasonable semantics for the convert-to-bool operation). Hence there is no need to pass a reference to the surrounding ValueEnv's myLetter, as it will never need to be changed.

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 4

The reference letter_ref refers to the myLetter pointer in the ValueEnv envelope containing the current ValueLet letter. That member is the only pointer to the current letter. If this pointer were not deleted before being reassigned, after the assignment there would be no pointer to the old letter, and the memory allocated for that object would "leak¤".

ValueLet::assign() is called (from ValueEnv::operator=) like this:

        myLetter->assign(myLetter,v.myLetter);

Hence, within assign(), the parameter letter_ref points to the current object (that is *this). Therefore, deleting letter_ref deletes the current object (!)

The assign() member function will continue to execute successfully even though its this object to which the member function belongs has ceased to exist. However, because this no longer points to a valid object, after the delete it is not possible to refer to or access any members of the original object. Hence ValueLet::assign() must be carefully coded to obey this restriction.

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 5

To be able to store complex<double>s inside a Value object the following steps would be required:

  1. Add a private member (say Value::myVal_Complex) to store the new type of value (which can't be stored in the myVal union for the same reason that a string couldn't).

  2. Add a new enumerated value (say vComplex) to the ValueType enum.

  3. Add a new Value constructor which takes a complex<double> and initializes myVal_Complex and myType appropriately.

  4. Increment the value of the vCount constant.

  5. For each member function add a new case to the corresponding switch statement. Add multiple cases to the switch inside Value::operator+=(), corresponding to the various combinations of valid left and right operands.

To be able to store complex<double>s inside a ValueEnv object the following steps would be required:

  1. Derive a new class (say ValueComplex) from ValueLet. The new class will have one data member - a complex<double> called myVal.

  2. Redefine each member function of this new letter class to implement the appropriate semantics.

  3. Add a new ValueEnv constructor which takes a complex<double> and initializes myLetter by allocating a new ValueComplex.

  4. Modify any member function of the other letter classes which might have to cope with encountering a complex<double>. For example, ValueDouble::addassign() would need an extra case if it was to cope with the addition of a complex:

void ValueDouble::addassign (ValueLet*& letter_ref, const ValueLet* val) { if (typeid(*val) == typeid(ValueInt)) { myVal += static_cast<const ValueInt*>(val)->Val(); } else if (typeid(*val) == typeid(ValueDouble)) { myVal+=static_cast<const ValueDouble*>(val)->myVal; } else if (typeid(*val) == typeid(ValueComplex)) { complex<double> sum = myVal + static_cast<const ValueComplex*>(val)->Val(); delete letter_ref; letter_ref = new ValueComplex(sum); } else { delete letter_ref; letter_ref = new ValueNull; } }

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 6

You should note the following features in your trace:

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 7

The template specialization¤ ValueType<void> is not valid. This is because it is equivalent to the following code, which has (at least) three fatal errors, as indicated:

template <> class ValueType<void> : public ValueLet { public: ValueType(void val); // INVALID PARAMETER LIST

virtual ValueLet* copy(void) const;

virtual bool convertToBool (void) const;

virtual void preincrement (ValueLet*&);

virtual void addassign (ValueLet*&, const ValueLet*);

const void& Val(void) const; // NO SUCH THING AS void&

private: void myVal; // INVALID DECLARATION };

These problems could be overcome by explicitly specializing the entire ValueType<void> class, but it is easier and more consistent just to provide a "void-like object" class (that is, class Null) to overcome these problems.

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 8

Universal mutual friendship between specializations¤ of ValueType could be established by templating a suitable friend declaration:

template <class Type> class ValueType : public ValueLet { template <class OtherType> friend class ValueType<OtherType>;

public: // THE REST OF THE TEMPLATE, AS BEFORE };

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 9

The benefits of re-implementing the letters as a single template include:

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 10

To be able to store complex<double>s inside a ValueEnv object the following steps would now be required:

  1. Add a new ValueEnv constructor which takes a complex<double> and initializes myLetter by allocating a new ValueType< complex<double> >.

  2. Specialize any member function of ValueType< complex<double> > to implement any semantics not adequately handled by the templated defaults.

  3. Modify any other member function specialization¤ (for other types) which might have to cope with encountering a complex<double>. For example, ValueType<double>::addassign() would need an extra case if it was to cope with the addition of a complex number.

(End of solution)  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


Solution to Exercise 11

  1. The structure similarity of the various ValueEnv constructors naturally suggests replacing them with a single member template (except, of course, for ValueEnv::ValueEnv(void) and ValueEnv::ValueEnv(const char*), which do not conform to the general structural pattern).  
     
    Indeed, doing this would allow the ValueEnv class to store values of any type, since initializing a ValueEnv with an object of a given type, T, would then automatically generate the required constructor (ValueEnv::ValueEnv(const T&)) and thereby initalize the ValueEnv with a(n automatically generated) letter of type ValueType<T> able to store values of type T.  
     
    Such a template constructor would look like this:

class ValueEnv { public: template <class T> ValueEnv(const T& val) : myLetter(new ValueType<T>(val)) {}

ValueEnv::ValueEnv(void); ValueEnv::ValueEnv(const char*); ValueEnv::ValueEnv(const ValueEnv&);

//ETC. AS BEFORE };

  1. The most obvious limitation of this approach is that the types to be stored in such a ValueEnv must possess the necessary properties to correctly instantiate the various member functions of the ValueType template. That is, type T must specify operators for conversion-to-bool, preincrement, add-assignment, and assignment.  
     
    A deeper problem is that there is no way to automatically specify interactions between new (autogenerated) types of letters and those we have already provided for (although explicit specializations¤ could help in some cases). This limitation might lead to some subtle errors. For example, the following code would result in res becoming "null", not 1+i as might be reasonably expected.  
     
    This is because, when intval-2 is added to val, a ValueType< complex<int> > is generated. Unfortunately, the existing ValueType<int> letter inside val has no way of knowing how to add a ValueType< complex<int> > to itself, and so the add-assign operation will default to replacing the letter inside val with a ValueType<Null>.

ValueEnv val; ValueEnv res (2);

complex<int> intval (1,1);

val += intval-2; res += val;

 


This material is part of the GCO4020/CSC428 - Advanced Object Oriented Techniques In C++ course.
Copyright © Damian Conway, 1997. All rights reserved.

Last updated: Fri Feb 18 11:17:42 2000