Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Java Style Vector Class in C++

0.00/5 (No votes)
17 Dec 2002 1  
An introduction to a vector collection class for typed pointers.

Introduction

This article will introduce you to a C++ class that was created to store and manage a collection of typed pointers. Its purpose is to easily manage a collection of object pointers with a familiar Java-like interface. This article is the first in several that I plan to post, describing and sharing some of the useful implementations that I have used extensively over the years.

Background

Before MFC, storing and managing a collection of object pointers was widely used in the software that I was developing. The software was not written for MFC and so, the MFC collection objects were not available. I could use STL, but design criteria prevented me from using such templates. A class designed to provide this support was constructed and used in application development. As time went by, its design was added and pruned to keep the best functionality in the class. Once project work came along in Java, I was quite taken by the clarity of the interface and its approach to class and method naming. If there is one thing I don't like, its cryptic, obfuscating code. What's the point of sharing code if you get a headache just sorting it out? Its a bit like reading a dry physics book.

I decided to carry this approach over to the collection of support classes I now use and this is the result of one such effort.

Why not STL?

For clarity, we had to develop software modules (source) that could be used by one client (customer) in Java, and another client (customer) in MFC. This meant parallel development and no STL, so we needed a set of core classes in C++ that were as similar as possible to Java, thus making the transition easier and saving money.

The ZVector class

The vector class, as its name suggests is a vector list manager that mimics much of the Java look and feel. There is a simple 'Z' prefix to the class to prevent conflict in the namespace. Functionally, it is capable of handling the same problems that are commonly done by the CTypePtrArray. The primary difference is, it is much easier to read and use with the Java-like interface. Another difference is, it is written to be pure C++, with no dependence on the operating system or the Windows API (stdafx.h optional). Hence, it should be portable to any OS. (I say 'should', because I have not actually used it in another OS). The class is not very big, and that's just the point. Keep it simple.

The solution

My approach to the solution is straightforward. First a class was created to manage the manipulation of the type neutral pointers. These pointers can be pointers to any type that the user desires. Its key purpose is to provide memory allocation and memory access functions for the vector list. It also optimizes these processes to reduce unneeded reallocations and thus improve speed. In addition, it takes a very careful approach to tracking the object, much like its Java counterpart.

Here is the vector manager class:

class ZS_EXPORT ZVectorBuffer 
   {
   //----------------------------------

   // Instance variables

   //----------------------------------

   private:
      static   Ptr            pNull;

   //----------------------------------

   // Instance variables

   //----------------------------------

   protected:
               Ptr         *  pPtr;
               int            iDataCount;
               int            iDataCapacity;
               int            iDataIncrement;

   //----------------------------------

   // Constructor/Destructor

   //----------------------------------

   private:
                              ZVectorBuffer(const ZVectorBuffer &array);
   public:
                              ZVectorBuffer(int initial=0,int increment=8);
                             ~ZVectorBuffer();

   //----------------------------------

   // Instance methods

   //----------------------------------

   private:
               void           operator = (const ZVectorBuffer &that);
               bool           move(int from_index, int to_index);
   public:

               void           ensureCapacity(int elements);
               void           setSize(int size);

               int            size()                                 const;
               int            capacity()                             const;
               int            indexOf(CPtr ptr,int start=0)          const;
               bool           contains(CPtr ptr)                     const;
               bool           isEmpty()                              const;
               void           copy(const ZVectorBuffer &that);
               void           append(const ZVectorBuffer &that);

               Ptr         &  getElement(int i)                      const;
               void           addElement(Ptr ptr);
               bool           mergeElement(Ptr ptr);
               void           removeElement(Ptr ptr);
               void           removeElementAt(int i);
               void           removeAll();

               void           sort(int (*compare)(CPtr, CPtr));

               bool           raise (CPtr ptr);
               bool           lower (CPtr ptr);
               bool           toHead(CPtr ptr);
               bool           toTail(CPtr ptr);
   };

You will notice in examining the class that there is a short form of char * called Ptr and const char * called CPtr. This is a typedef that is simply used to make the code more readable. The second class is that class that you will actually use when creating one of these objects. It is the template class called ZVector. Its purpose is to create instances of typed lists and provide the proper typecasting when the object is used, so you don't have to. Here it is:

template<class T> class ZVector : public ZVectorBuffer
   {
   public:
      T* &      elementAt(int i)       const { return (T*&)getElement(i);  }
      T* &      operator [] (int i)    const { return (T*&)getElement(i);  }
      T* &      firstElement()         const { return (T*&)getElement(0);  }
      T* &      lastElement() const { return (T*&)getElement(iDataCount-1);}

   public:
      void      operator << (T *  t)         { addElement(t);    }
      void      operator >> (T *  t)         { removeElement(t); }
      void      setElementAt(T * t,int i)    { pPtr[i] = t;     }
   };

This class is constructed purely inline, providing the primary operator that would be used in the application. If you look closely, you will also notice a dummy static pointer installed in the vector manager class. Since the access operator requires a pointer reference, the dummy pointer is used to satisfy this condition if the index is out of bounds. Assigning this is harmless and I would rather do this than involve exceptions at such a low level.

Using the ZVector class

Finally, I will end up with a couple of code snippets to show how the class would be used in a typical application.

In the declaration, the representation is quite simple. Instantiate a template object in the class definition or body of code and then use it. Rarely would one create the object dynamically, and I have never found a need for it.

   //----------------------------------

   // Instance variables

   //----------------------------------

   private:
      ZVector <MyClass>     vChildren;

Or...

void some_method()
   {
   ZVector<MyClass> vector;       // Create a typed vector


   vector.addElement(new MyClass());    // Add one object

   vector.addElement(new MyClass());    // Add another object (unique)


   MyClass *object = vector[1];         // Access second object


   vector.removeElement(object);        // Remove the second object

   delete object;                       // Delete the second object


   object = vector[0];                  // Access first object

   delete object;                       // Delete the first object

   }

Conclusions

Simple but effective. It is not fool-proof thought. If you tried really hard you could break it, but in everyday use it is bulletproof. In almost all cases I use the template operator exclusively and only a few methods from the manager class. I have approximately 7-10 programmers and engineers using it on a daily basis and it has not caused any problems. I believe its performance to be very good, although I have not put it up against other classes in benchmarks. I welcome any information on this.

This is a core class. In future I will show some derived implementations using this as the base class. They also will contain a Java-like interface to make them look and feel very familiar.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here