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:
  1. Support as many types as possible,
  2. 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
  3. 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:
  1. A copy constructor - because of FixedArray's compiler-implemented copy constructor.
  2. A copy assignment - because of FixedArray's compiler-implemented copy assignment.
  3. 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: