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
{
private:
static Ptr pNull;
protected:
Ptr * pPtr;
int iDataCount;
int iDataCapacity;
int iDataIncrement;
private:
ZVectorBuffer(const ZVectorBuffer &array);
public:
ZVectorBuffer(int initial=0,int increment=8);
~ZVectorBuffer();
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.
private:
ZVector <MyClass> vChildren;
Or...
void some_method()
{
ZVector<MyClass> vector;
vector.addElement(new MyClass());
vector.addElement(new MyClass());
MyClass *object = vector[1];
vector.removeElement(object);
delete object;
object = vector[0];
delete 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.