Professional C__ - Marc Gregoire [199]
The class also prevents assignment and pass-by-value because of the mCurElem data member. To make assignment and pass-by-value work you would have to implement an assignment operator and copy constructor and make sure mCurElem is valid in the destination object. This is omitted in this example.
The implementation of the RoundRobin class follows with comments explaining the code. Note the use of reserve() in the constructor, and the extensive use of the iterator in add(), remove(), and getNext(). The trickiest aspect is keeping mCurElem valid and referring to the correct element following calls to add() or remove().
template RoundRobin { // If the client gave a guideline, reserve that much space. mElems.reserve(numExpected); // Initialize mCurElem even though it isn't used until // there's at least one element. mCurElem = mElems.begin(); } template RoundRobin { // nothing to do here -- the vector will delete all the elements } // Always add the new element at the end template void RoundRobin { // Even though we add the element at the end, the vector could // reallocate and invalidate the iterator with the push_back call. // Take advantage of the random access iterator features to save our // spot. int pos = mCurElem - mElems.begin(); // add the element mElems.push_back(elem); // Reset our iterator to make sure it is valid. mCurElem = mElems.begin() + pos; } template void RoundRobin { for (auto it = mElems.begin(); it != mElems.end(); ++it) { if (*it == elem) { // Removing an element will invalidate our mCurElem iterator if // it refers to an element past the point of the removal. // Take advantage of the random access features of the iterator // to track the position of the current element after removal. int newPos; // If current iterator is before or at the one we're removing, // the new position is the same as before. if (mCurElem <= it) { newPos = mCurElem - mElems.begin(); } else { // otherwise, it's one less than before newPos = mCurElem - mElems.begin() - 1; } // erase the element (and ignore the return value) mElems.erase(it); // Now reset our iterator to make sure it is valid. mCurElem = mElems.begin() + newPos; // If we were pointing to the last element and it was removed, // we need to loop back to the first. if (mCurElem == mElems.end()) { mCurElem = mElems.begin(); } return; } } } template T& RoundRobin { // First, make sure there are any elements. if (mElems.empty()) { throw std::out_of_range("No elements in the list"); } // retrieve a reference to return T& retVal = *mCurElem; // Increment the iterator modulo the number of elements ++mCurElem; if (mCurElem == mElems.end()) { mCurElem = mElems.begin(); } // return the reference return retVal; } Code snippet from RoundRobin\RoundRobin.h Here’s a simple implementation of a scheduler that uses the RoundRobin class template, with comments explaining the code. // Simple Process class. class Process { public: // Constructor accepting the name of the process. Process(const string& name) : mName(name) {} // Implementation of doWorkDuringTimeSlice would let the process // perform its work for the duration of a time slice. // Actual implementation omitted. void doWorkDuringTimeSlice() { cout << "Process " << mName << " performing work during time slice." << endl; } // Needed for the RoundRobin::remove method to work. bool operator==(const Process& rhs) { return mName == rhs.mName; } protected: string mName; }; // Simple round-robin based process scheduler. class Scheduler { public: // Constructor takes a vector of processes. Scheduler(const vector