I have steered away from using templates and nested classes despite there being a few places where normal c++ practice would mandate their use. This is to avoid problems with compilers that lack or poorly implement these features.
Whilst in 2004, templates are well-implemented by mainstream compilers, the core OOFILE will continue to follow this minimalist constraint. Outer layers and add-on products may use templates.
internal classes are prefixed by OOF_
external classes are prefixed by db
static members start with an 's' followed by an uppercase letter
all other members start with an 'm' followed by an uppercase letter
constants start with 'k' followed by an uppercase letter
local variables start with a lowercase word
parameters in header files are only named where the name adds to the readability
eg: dbQuery(dbQuery&) - not needed in copy constructor
dbfieldSeg(dbField&, unsigned int len) - len is needed
public functions start with a lowercase letter, private & protected functions start with an Uppercase
User database CONVENTIONS
dbField members start with an uppercase letter, eg dbChar LastName;
Where a dynamically allocated member is "owned" by a class, meaning
the class is responsible for it's allocation & deallocation, the member
next to its declaration.
There are rare cases where a method transfers ownership of a member, or exposes a pointer. The names of these methods will have "adopt" or "expose" in them, or in a comment next to the name (eg: an operator char*).
Any time you see a pointer parameter which has "adopted" in the name it means you are no longer responsible for deleting that pointer.
Similarly, when a parameter or method has "orphan" in the name it means you are being returned a pointer for which your code is now responsible to delete.
There are a lot of pointer members in OOFILE which are purely references to related classes. Marking relevant pointers with //owned makes it easy to see which ones should be cleaned up in destructors.
This section tries to summarise some of the c++ techniques I've used. I urge you to purchase Effective C++ but the following may help if you are fairly new to C++ and a little confused by some of OOFILE's code.
In any library for general re-use, globals have the potential to cause conflicts and "pollute the namespace". They also suffer from uncontrolled access - anyone can fiddle with them. I use Static Class Members instead, which are global variables belonging to a specific class (eg: dbConnect::currentlyConstructing). They have to be explicitly qualified by the class name and you can restrict who has access using either Friend Classes or Friend Functions.
OOFILE_Dictionary and OOFILE_DictRep form a pair, where the outer class forwards nearly all its function to the inner (except for iteration).
This allows multiple copies of OOFILE_Dictionary to point to a single copy of the actual data, in OOFILE_DictRep, at the cost of some extra forwarding logic. Note that any copy constructors or assignments that create a new OOFILE_Dictionary must update the reference count of the DictRep.
The forwarding is not as onerous as it may appear - use of inlines reduces it to little more than one pointer derefence over the normal use of virtual object.
I've overloaded operator (the array index) in three ways for the OOFILE_Dictionary. Notice that I've provided both unsigned int and signed int, as well as the named access.
There is a minor problem in providing a native unsigned int, without a signed int version, in that the literal constant 0 is typed as a signed int. By the c++ conversion rules, the literal constant 0 is one trivial conversion away from either a null pointer to char, or an unsigned int.
Thus, the ambiguity cannot be resolved. Providing an operator with a signed int (inlined for performance), gives an operator that can be called with no trivial conversions. Being a slightly better choice, therefore the compiler is happy that literal 0 will invoke the [signed int] version.
I've made OOFILE_DictRep a "lazy" array in that the default constructor
doesn't declare any space and there is no requirement for users of the class
to explicitly tell it to expand. If you look into the operator you'll notice
it performs a range check and does an expansion. The expansion provides a null
pointer in the target cell, so use of an out of range value on the rhs is legal
myField = mFields[outOfRangeIndex];
(c) Copyright A.D. Software 1994-2004 (All Rights Reserved).
Last Updated: 21st June 2004