FixedArray
Array Implementations -->
FixedArray class
Introduction
A fixed array is an array with a fixed size (known in compilation time), usually allocated
on the stack. For example, the following statement allocates a native (C-style) fixed array of
integers on the stack:
int array[30];
Requirements From A FixedArray Class
For a FixedArray class to be useful, it must:
- Support as many types as possible,
- be able to replace a native fixed array at as many places as possible, meaning it
should at least support all operations a native array supports, and
- be exception safe.
The Implementation
The following is the straight-forward implementation of a class template for a fixed
array. FixedArray uses an privately-inherited base class (FixedArrayBase) to avoid
code bloat. It is a common technique with templates:
template <typename T> struct FixedArrayBase {
FixedArrayBase(const size_type sz) : arr_(new T[sz]) {}
~FixedArrayBase() { delete[] arr_; }
void fill_from(const FixedArrayBase& other, const size_type sz)
{
for (size_type i=0; i < sz; ++i)
{
arr_[i] = other.arr_[i];
}
}
T* arr_;
private:
// Avoid copies
FixedArrayBase(const FixedArrayBase&);
FixedArrayBase& operator=(const FixedArrayBase&);
};
template <typename T, size_type N>
class FixedArray : private FixedArrayBase<T> {
public:
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
FixedArray() : FixedArrayBase<T>(N) {}
FixedArray(const FixedArray& other) : FixedArrayBase<T>(N)
{
fill_from(other, size());
}
FixedArray& operator=(const FixedArray& other)
{
FixedArray temp(other);
std::swap(arr_, temp.arr_);
return *this;
}
iterator begin() { return arr_; }
iterator end() { return &arr_[size()]; }
const_iterator begin() const
{ return const_cast<FixedArray&>(*this).begin(); }
const_iterator end() const
{ return const_cast<FixedArray&>(*this).end(); }
size_type size() const { return N; }
// Unchecked references
reference operator[] (const size_type n) { return arr_[n]; }
const_reference operator[] (const size_type n) const
{ return const_cast<FixedArray&>(*this)[n]; }
// Checked references
reference at(const size_type pos)
{
if (pos >= size())
throw OutOfRange();
return (*this)[pos];
}
const_reference at(const size_type pos) const
{ return const_cast<FixedArray&>(*this).at(pos); }
};
Let's examine how the class template answers the requirements:
Requirements From The Stored Type, T
There is one explicit requirement imposed by FixedArray on the stored type, T - a
default constructor. It is a reasonable requirement, since a native C++ array also requires
it if an initializer list is not specified for the array.
There are 3 implicit requirements from T:
- A copy constructor - because of FixedArray's compiler-implemented copy constructor.
- A copy assignment - because of FixedArray's compiler-implemented copy assignment.
- A destructor.
Exception Safety
Strong exception safety is demonstrated here "naturally" because T's only used
operation is the default constructor while constructing the FixedArray object. This
means that if, while constructing a FixedArray object, T's constructor will throw then
the FixedArray object will be deallocated automatically (and will never exist in
inconsistent state).
There is also one requirement imposed by FixedArray: An exception-safe copy
assignment. Consider the following code where 2 FixedArarys are used to hold a
type with an unsafe copy assignment, UnsafeType:
FixedArray<UnsafeType, 5> arr1, arr2;
arr1 = arr2; // (*)
The line marked (*) can throw and leave arr1 in a state where one of the objects in the
array is invalid. In this case, FixedArray conforms only to the basic guarantee of
exception safety.
Disadvantages
There are some disadvantages to using FixedArray:
- Initializer lists cannot be used with FixedArray as with native arrays. For example, the
following code sample cannot be replaced with FixedArray:
Complex complex_array[3] = {Complex(1,3), Complex(3,-4), Complex(0,0) };
- Code bloat. The compiler writes different code for each type and size specified in
FixedArray's template arguments.
- There is no real advantage to using a C++ fixed array class. Its performance in terms of
excecution speed is arguably slighly better than a dynamic array, and it lacks the flexibility
of a dunamic array.