The Object class hierarchy ADT hierarchy Object Container
The Object class hierarchy
ADT hierarchy Object Container Stack. As. Array Stack. As. Linked. List Queue. As. Array Queue. As. Linked. List Priority. Queue Graph Tree
ADT hierarchy (con’t) Object Container Searchable Container Search. Tree List BST In. Order. List. As. Array List. As. Linked Hash. Table Chained. HT
Abstract classes z specify only an interface, no implementation z cannot be used to instantiate objects z are used to build other classes through inheritance z intended as the base class from which others are derived z often contain ‘pure virtual member functions’ xcontain only interface, no implementation xthe base class interface allows you to access the derived class member functions xallows for high levels of polymorphism
Concrete classes z. Derived from base classes through inheritance z. Can be used to create many different implementations of an ADT ysub ADTs
Object class hierarchy Object In object-oriented languages, all ADTs can be seen to be derived from a single ancestor - the Object ADT. Stored in this class definition would be all the essential qualities that any object must have. But what might the member functions, etc. be?
Object class members Object 1. We will need to compare any two variables of the same derived class. (compareto) 2. We will need to determine whether an object contains anything. (is. Null) 3. We will need to compare any two variables that are derived from the Object class. (compare) 4. We will need to send the contents of an object into an output stream for printing (put) 5. We will need the normal overloaded operators
Object class members Object class Object { protected: virtual int Compare. To (Object const&) const = 0; public: virtual ~Object(); virtual bool Is. Null() const; virtual int Compare(Object const&) const; virtual void Put(ostream&) const = 0; }
Virtual functions z. Object is an abstract base class. This means that it cannot be directly instantiated. Instead, we will make derived classes that inherit the base class and instantiate them. z. Instances of derived class objects will be accessed through the base class interface. z. The base class interface therefore needs to be virtual (as opposed to substantial)
Virtual destructor z. For example, the base class destructor is virtual. Note that it doesn’t do anything. z. The derived class destructor is called through the base class interface. z. The same goes for all other virtual functions. The base class version is called first and may be ‘overridden’ by the derived class function of the same name.
Object class virtual methods #include <typeinfo> Object: : ~Object() {} // Destructor bool Object: : Is. Null() const { return false; } int Object: : Compare(Object const& object) const { Compare uses Compare. To to compare two objects of the same if (typeid(*this) == typeid(object)) derived type. It returns negative, 0 return Compare. To(object); else if (typeid(*this). before(typeid(object))) or positive values (like strcmp) return -1; typeid is a C++ operator returning a reference else to an instance of the type_info class (defined in return 1; header file <typeinfo>. This class has a } member function called name() which returns a pointer to a character string that contains the name of the class. The before() method refers to the inheritance tree.
Pure virtual functions contain no implementation (=0). This means they will need to be defined in every concrete class derived from Object. class Object { protected: virtual int Compare. To (Object const&) const = 0; public: virtual ~Object(); virtual bool Is. Null() const; virtual int Compare(Object const&) const; virtual void Put(ostream&) const = 0; }
Base class operators z. Objects need to be compared. z. Now that we have established a mechanism for doing this (Compare and Compare. To) we can use these to build our overloaded operator definitions. z. Similarly, we can use Put to built an overloaded << operator
Object class operators inline bool operator==(Object const& left, Object const& right) { return left. Compare(right) == 0; } inline bool operator!=(Object const& left, Object const& right) { return left. Compare(right) != 0; } inline bool operator<=(Object const& left, Object const& right) { return left. Compare(right) <= 0; } inline bool operator<(Object const& left, Object const& right) { return left. Compare(right) < 0; } inline bool operator>=(Object const& left, Object const& right) { return left. Compare(right) >= 0; } inline bool operator>(Object const& left, Object const& right) { return left. Compare(right) > 0; } inline bool operator<<(ostream& s, Object const& object) { object. Put(s); return s; }
Result z. This completes a definition for our Object ADT as base class from which other classes may be derived. z. Since Object-oriented programming is all about Objects, there must be many derived class possibilities.
Base class hierarchy Object Null. Object Wrapper<T> Null. Object is a concrete class. It represents an Object that does not contain anything. Much like an empty set is a type of a set. Char Int Double String If we wanted to create objects that did contain things (like a native type item) we could create a ‘Wrapper’ class derived from Object. Then instantiate Char objects, Int objects, Double objects, etc.
Base class hierarchy Object Null. Object Wrapper<T> However, It is usually much easier to use integers, doubles, etc. directly rather than wrapping them in Object classes. These are wrapper classes named for a native type they will ‘wrap up’. Char Int Double String
Base class hierarchy Object Null. Object Wrapper<T> Char Int Container Double String A more practical ADT that derives from Object is the Container.
Container class inheritance Object Container A Container is an object that holds other objects within it. Since Containers are objects themselves, they are derived from the Object base class. Container is also an abstract base class. Although it derives from Object, we do not intend to instantiate Containers directly. We will create other class definitions that inherit the Container (and hence Object) characteristics first.
Container class definition Class Container : public virtual Object { protected: unsigned int count; // number of items in Container(); public: virtual unsigned int Count() const; // accessor for count virtual bool Is. Empty() const; // empty container virtual bool Is. Full() const; // full container virtual void Put(ostream&) const; // replaces Object Put virtual Iterator& new. Iterator() const; virtual void Purge() = 0; // empties a container virtual void Accept(Visitor&) const = 0; }
Container class definition Container: : Container() : count(0) {}; // constructor unsigned int Container: : Count() const {return count; } bool Container: : Is. Empty() const { return Count() == 0; } bool Container: : Is. Full() const { return false; } The other functions are more complex than we need for now. Put overrides the Object. Put() pure virtual function. A ‘Visitor’ is an Object that interacts with Containers. If the Container accepts the Visitor as valid it will let it visit each Object it contains and run it’s methods on that Object (for example, the Visitor may check to see if the data in an Object matches data it is looking for.
Iterators An Iterator is also an Object. It’s purpose is to provide a method for visiting all of the Objects in a Container, one-by-one. For example: overloaded operators Some. Container c; Iterator& i = c. New. Iterator(); while (!i. Is. Done()) { cout << *i << endl; ++i; } delete &i;
Iterator abstract base class Class Iterator { public: virtual ~Iterator(); virtual void Reset() = 0; virtual bool Is. Done() const = 0; virtual Object& operator*() const = 0; virtual void operator++() = 0; };
ADT hierarchy Object Container Stack. As. Array Stack. As. Linked. List Queue. As. Array Queue. As. Linked. List Both the Stack ADT and Queue ADT are Containers. They can be written as derived classes of the Container abstract base class we have developed.
Stack abstract base class Class Stack : public virtual Container { public: virtual Object& Top() const = 0; virtual void Push(Object&) = 0; virtual Object& Pop() = 0; };
Stack. As. Array Class Stack. As. Array : public Stack { private: Array<Object*> array; class Iter; public: Stack. As. Array(unsigned int); ~Stack. As. Array(); void Purge(); void Push(Object& object); Object& Pop(); Object& Top() const; friend class Iter; }; Uses the Array template to create an array of pointers to Objects. The size of the array will be determined by the constructor. This is a nested class definition. Note how it uses the friend designation.
Stack. As. Array Iter class Stack. As. Array: : Iter : public Iterator { private: Stack. As. Array const& stack; unsigned int position; public: Iter(Stack. As. Array const&); // …other methods it may need to overload, etc. };
Stack. As. Array methods Stack. As. Array: : Stack. As. Array(unsigned int size) : array(size) {}; Used by destructor to deallocate array pointers. void Stack. As. Array: : Purge() { if (Is. Owner()) for (unsigned in i=0; i < count; i++) delete array[i]; count = 0; } Stack. As. Array: : ~Stack. As. Array() { Purge(); }
Stack. As. Array methods (con’t) void Stack. As. Array: : Push(Object& object) { assert(count != array. Length()); array[count++] = &object; } Object& Stack. Array: : Pop() { assert(count > 0); return *array[--count]; } Object& Stack. As. Array: : Top() const { assert(count > 0); return *array[count - 1]; // why not count--? }
Stack. As. Array Iterator Stack. As. Array: : Iter(Stack. As. Array conts& _stack) : stack(_stack) { Reset(); } bool Stack. As. Array: : Iter: : Is. Done() const { return position >= stack. count; } Object& Stack. Array: : Iter: : operator*() const // dereferencing operator { if (position < stack. count) return *stack. array[position]; else return Null. Object: : Instance(); } void Stack. As. Array: : Iter: : operator++() { if (position < stack. count) ++ position; } void Stack. As. Array: : Iter: : Reset() { position = 0; }
Use of Iterator object Stack. As. Array stack; stack. Push(*new Int(3)); // int in wrapper Int, an Object stack. Push(*new Int(1)); stack. Push(*new Int(4)); Iterator& i = stack. New. Iterator(); while (!i. Is. Done()) { cout << *i << endl; // uses overloaded dereference ++i; } delete &i;
Stack. As. Linked. List Class Stack. As. Linked. List : public Stack { private: Linked. List<Object*> list; // a linked list of pointers to Objects class Iter; public: Stack. As. Linked. List(); void Purge(); ~Stack. As. Linked. List(); void Push(Object& object); Object& Pop(); Object& Top() const; friend class Iter; }
Stack. As. Linked. List methods Stack. As. Linked. List: : Stack. As. Linked. List() : list() {}; // constructor void Stack. As. Linked. List: : Purge() { if (Is. Owner()) { List. Element<Object*> const* ptr; for (ptr = list. Head(); ptr != 0; ptr=ptr->Next()) delete ptr->Data() } list. Purge(); count = 0; } Stack. As. Linked. List: : ~Stack. As. Linked. List() { Purge(); }
Stack. As. Linked. List methods void Stack. As. Linked. List: : Push(Object& object) { list. Prepend(&object); ++count; } Object& Stack. As. Linked. List: : Pop() { assert(count > 0); Object& result = *list. First(); list. Extract(&result); --count; return result; } Object& Stack. As. Linked. List: : Top() const { assert(count > 0); return *list. First(); }
Stack. As. Linked. List methods Stack. As. Linked. List: : Iter (Stack. As. Llinked. List const& _stack): stack(_stack) { Reset(); } bool Stack. As. Linked. List: : Iter: : Is. Done() const { return position == 0; } Object& Stack. As. Linked. List: : Iter: : operator*() const { if (position != 0) return *position->Data(); else return Null. Object: : Instance(); } void Stack. As. Linked. List: : Iter: : operator++() { if (position != 0) position = position->Next(); } void Stack. As. Linked. List: : Iter: : Reset() { position = stack. list. Head(); }
Use of Iterator object Stack. As. Linked. List stack; stack. Push(*new Int(3)); stack. Push(*new Int(1)); stack. Push(*new Int(4)); Iterator& i = stack. New. Iterator(); while (!i. Is. Done()) { cout << *i << endl; ++i; } delete &i;
- Slides: 36