//-------------------------------------------------------------------- // // arrays.cpp // // An example of classes that implement more robust arrays than are // natively implemented in C/C++. The example illustrates inheritance // and the use of the "friend" keyword. // // The example make extensive use of pointers and dynamic memory // management, so if this is something you are not familar with, you // might want to come back to this program once we have covered // pointers. // // Reworked by: Simon Parsons // Last modified: 14th March 2009 #include using namespace std; //-------------------------------------------------------------------- // // IntArray // // The class provides an implmentation of an array of integers, but one // that prevents us reading or writing outside of the array, class IntArray { // friend class IntArrayIter; // Allow IntArrayIter to have access to data private: int *elems; // The private data of IntArray is an array of ints size_t numElems; // The data member is a pointer so that we can change // the size of the array dynamically. public: void init(); void cleanup(); void setSize( size_t value ); size_t getSize(); void setElem( size_t index, int value ); int getElem( size_t index ); }; // Initialise the array void IntArray::init() { numElems = 0; elems = 0; } // Delete everything in the array void IntArray::cleanup() { free( elems ); elems = 0; numElems = 0; } // Set or reset the size of the array, deleting any existing space that // is reserved, and reserving enough space in memory. void IntArray::setSize( size_t value ) { if ( elems != 0 ) { free( elems ); } numElems = value; elems = (int *)malloc( value * sizeof( int )); } // How big is the array currently? size_t IntArray::getSize() { return( numElems ); } // Set a specific element of the array to value. void IntArray::setElem( size_t index, int value ) { if (( index < -0 ) || ( index >= numElems )) { cerr << "bad index"; exit( 1 ); } elems[index] = value; } // Get the value of a specific element of the array int IntArray::getElem( size_t index ) { if (( index < -0 ) || ( index >= numElems )) { cerr << "bad index"; exit( 1 ); } return( elems[index] ); } // // end of IntArray //-------------------------------------------------------------------- // // IntArrayIter // // A way of adding functionality to IntArray. It has an IntArray as a // data element, and defines some additional functions that operate // on it. // // These additional functions make this into a "iterator" (hence the // name), a data structure which steps through its content item by // item. class IntArrayIter { private: IntArray *myArray; // The main data element of the class is an size_t curItem; // IntArray, or a sub-class of IntArray. public: void init(); void cleanup(); void setIter( IntArray *myArray ); void goFirst(); void goNext(); int getCur(); int curIsValid(); }; // Initialise, using the accessor function of its member myArray. void IntArrayIter::init() { myArray->init(); } // Delete everyting in myArray. void IntArrayIter::cleanup() { myArray->cleanup(); myArray = 0; } // Set myArray to point to a new set of values. void IntArrayIter::setIter( IntArray *myArray ) { this->myArray = myArray; goFirst(); } // Start at the beginning of the array void IntArrayIter::goFirst() { curItem = 0; } // Go to the next item of myArray. void IntArrayIter::goNext() { curItem++; } // Extract the current value of myArray. int IntArrayIter::getCur() { if ( ! curIsValid() ) { cerr << "no current item\n"; exit( 1 ); } return curItem; } // Check that we are accessing a valid item. // // The fact that we need to access the private datamember numElems of // the IntArray myArray is the (only) reason that IntArrayInter needs // to be a friend of IntArray. // // We could, of course, replace this reference to numElems with a call // to getSize(), and then no friend relation would be required. int IntArrayIter::curIsValid() { return (( myArray != 0 ) && ( curItem >= 0 ) && ( curItem < myArray->numElems )); } // // end of IntArrayIter //-------------------------------------------------------------------- // // StatsIntArray // // A sub-class of IntArray that provides some statistics on the numbers // in the array. class StatsIntArray : public IntArray { private: int *buf; public: int mean(); }; // Find the mean of the array by using IntArrayIter. This shows that // StatsIntArray has access to all the functions of IntArray and also // how the functions of IntArrayIter can be used to iterate through the // members of the data structure without having to explicitly handle an // index. int StatsIntArray::mean() { int total = 0; IntArrayIter iter; iter.setIter( this ); for ( iter.goFirst(); iter.curIsValid(); iter.goNext() ) { total += getElem( iter.getCur() ); } return( total / getSize() ); } // // end of StatsIntArray //-------------------------------------------------------------------- // int main() { // Set up an StatsIntArray called powersOf2, and an IntArrayIter // iter which has that as its data element. StatsIntArray powersOf2; IntArrayIter iter; powersOf2.init(); powersOf2.setSize( 8 ); powersOf2.setElem( 0, 1 ); iter.setIter( &powersOf2 ); iter.goFirst(); // Use iter to step through powersOf2, doubling each element to make the // next. for ( iter.goNext(); iter.curIsValid(); iter.goNext() ) { powersOf2.setElem( iter.getCur(), 2*powersOf2.getElem( iter.getCur()-1 )); } cout << "here are the elements:\n"; // Use iter to step through the array again, now printing each element // out. for ( iter.goFirst(); iter.curIsValid(); iter.goNext() ) { cout << "powerOf2=" << powersOf2.getElem( iter.getCur() ) << "\n"; } // Use the mean() function of powersOf2 to compute the mean, then delete // all the data in both iter and powersOf2 cout << "mean = " << powersOf2.mean() << "\n"; iter.cleanup(); powersOf2.cleanup(); }