Let's zoom in and try to understand how the previous program works. The following code tells the C++ compiler that what follows is a class template:
template < class T >
class DynamicArray {
private:
deque< T > dynamicArray;
typename deque< T >::iterator pos;
As you can see, the DynamicArray class makes use of STL deque internally, and an iterator for deque is declared with the name, pos. This iterator, pos, is utilized by the Dynamic template class to provide high-level methods such as the initialize(), appendValue(), hasNextValue(), and getValue() methods.
The initialize() method initializes the deque iterator pos to the first data element stored within deque. The appendValue( T element ) method lets you add a data element at the end of deque. The hasNextValue() method tells whether the DynamicArray class has further data values stored--true indicates it has further values and false indicates that the DynamicArray navigation has reached the end of deque. The initialize() method can be used to reset the pos iterator to the starting point when required. The getValue() method returns the data element pointed by the pos iterator at that moment. The getValue() method doesn't perform any validation; hence, it must be combined with hasNextValue() before invoking getValue() to safely access the values stored in DynamicArray.
Now, let's understand the main() function. The following code declares a DynamicArray class that stores the int data type; DynamicArray<int> intArray will trigger the C++ compiler to instantiate a DynamicArray class that is specialized for the int data type:
DynamicArray<int> intArray;
intArray.appendValue( 100 );
intArray.appendValue( 200 );
intArray.appendValue( 300 );
intArray.appendValue( 400 );
The values 100, 200, 300, and 400 are stored back to back within the DynamicArray class. The following code ensures that the intArray iterator points to the first element. Once the iterator is initialized, the values stored in the DynamicArray class are printed with the getValue() method, while hasNextValue() ensures that the navigation hasn't reached the end of the DynamicArray class:
intArray.initialize();
cout << "nInt DynamicArray values are ..." << endl;
while ( intArray.hasNextValue() )
cout << intArray.getValue() << "t";
cout << endl;
Along the same lines, in the main function, a char DynamicArray class is created, populated with some data, and printed. Let's skip char DynamicArray and directly move on to the DynamicArray class that stores bool.
DynamicArray<bool> boolArray;
boolArray.appendValue ( "1010" );
boolArray.initialize();
cout << "nBool DynamicArray values are ..." << endl;
while ( boolArray.hasNextValue() )
cout << boolArray.getValue() << "t";
cout << endl;
From the preceding code snippet, we can see everything looks okay, right? Yes, the preceding code works perfectly fine; however, there is a performance issue with the DynamicArray design approach. While true can be represented by 1 and false can be represented by 0, which requires just 1 bit, the preceding DynamicArray class makes use of 8 bits to represent 1 and 8 bits to represent 0, which we must fix without forcing end users to choose a different DynamicArray class that works efficiently for bool.
Let's fix the issue by using explicit class template specialization with the following code:
#include <iostream>
#include <bitset>
#include <algorithm>
#include <iterator>
using namespace std;
template <>
class DynamicArray<bool> {
private:
deque< bitset<8> *> dynamicArray;
bitset<8> oneByte;
typename deque<bitset<8> * >::iterator pos;
int bitSetIndex;
int getDequeIndex () {
return (bitSetIndex) ? (bitSetIndex/8) : 0;
}
public:
DynamicArray() {
bitSetIndex = 0;
initialize();
}
~DynamicArray() { }
void initialize() {
pos = dynamicArray.begin();
bitSetIndex = 0;
}
void appendValue( bool value) {
int dequeIndex = getDequeIndex();
bitset<8> *pBit = NULL;
if ( ( dynamicArray.size() == 0 ) || ( dequeIndex >= ( dynamicArray.size()) ) ) {
pBit = new bitset<8>();
pBit->reset();
dynamicArray.push_back ( pBit );
}
if ( !dynamicArray.empty() )
pBit = dynamicArray.at( dequeIndex );
pBit->set( bitSetIndex % 8, value );
++bitSetIndex;
}
bool hasNextValue() {
return (bitSetIndex < (( dynamicArray.size() * 8 ) ));
}
bool getValue() {
int dequeIndex = getDequeIndex();
bitset<8> *pBit = dynamicArray.at(dequeIndex);
int index = bitSetIndex % 8;
++bitSetIndex;
return (*pBit)[index] ? true : false;
}
};
Did you notice the template class declaration? The syntax for template class specialization is template <> class DynamicArray<bool> { };. The class template expression is empty <> and the name of the class template that works for all data types and the name of the class that works the for the bool data type are kept the same with the template expression, <bool>.
If you observe closely, the specialized DynamicArray class for bool internally makes use of deque< bitset<8> >, that is, deque of bitsets of 8 bits, and, when required, deque will automatically allocate more bitset<8> bits. The bitset variable is a memory-efficient STL container that consumes just 1 bit to represent true or false.
Let's take a look at the main function:
#include "dynamicarray.h"
#include "dynamicarrayforbool.h"
int main () {
DynamicArray<int> intArray;
intArray.appendValue( 100 );
intArray.appendValue( 200 );
intArray.appendValue( 300 );
intArray.appendValue( 400 );
intArray.initialize();
cout << "nInt DynamicArray values are ..." << endl;
while ( intArray.hasNextValue() )
cout << intArray.getValue() << "t";
cout << endl;
DynamicArray<char> charArray;
charArray.appendValue( 'H' );
charArray.appendValue( 'e' );
charArray.appendValue( 'l' );
charArray.appendValue( 'l' );
charArray.appendValue( 'o' );
charArray.initialize();
cout << "nChar DynamicArray values are ..." << endl;
while ( charArray.hasNextValue() )
cout << charArray.getValue() << "t";
cout << endl;
DynamicArray<bool> boolArray;
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.appendValue ( true );
boolArray.appendValue ( true);
boolArray.appendValue ( false);
boolArray.appendValue ( false );
boolArray.appendValue ( true );
boolArray.appendValue ( true);
boolArray.appendValue ( false);
boolArray.appendValue ( false );
boolArray.initialize();
cout << "nBool DynamicArray values are ..." << endl;
while ( boolArray.hasNextValue() )
cout << boolArray.getValue() ;
cout << endl;
return 0;
}
With the class template specialization in place, we can observe from the following that the main code seems the same for bool, char, and double, although the primary template class, DynamicArray, and the specialized DynamicArray<bool> class are different:
DynamicArray<char> charArray;
charArray.appendValue( 'H' );
charArray.appendValue( 'e' );
charArray.initialize();
cout << "nChar DynamicArray values are ..." << endl;
while ( charArray.hasNextValue() )
cout << charArray.getValue() << "t";
cout << endl;
DynamicArray<bool> boolArray;
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.initialize();
cout << "nBool DynamicArray values are ..." << endl;
while ( boolArray.hasNextValue() )
cout << boolArray.getValue() ;
cout << endl;
I'm sure you will find this C++ template specialization feature quite useful.