I l@ve RuBoard |
![]() ![]() |
2.4 Using Local Static ObjectsOur fibon_seq() function of Section 2.2 calculates a Fibonacci sequence of a user-specified size with each invocation, returning a vector holding the elements. That is a bit more work than is necessary. We really need only one Fibonacci sequence vector. The elements, after all, are invariant. The only thing that changes from one call of fibon_seq() to the next is the number of elements the user wishes to have available. Consider the following three invocations of fibon_seq(): fibon_seq( 24 ); fibon_seq( 8 ); fibon_seq( 18 ); The first call calculates all the values necessary to fulfill the request of the second and third invocations. If a fourth invocation requested 32 elements, we really need calculate only elements 25 through 32 ?if we could cache the elements calculated between invocations. How might we do that? A vector object local to the function does not provide a solution. The local object is created with each invocation of the function and is discarded as soon as the function terminates. A tempting alternative is to define a vector object at file scope. It is always tempting to introduce an object at file scope to solve a communications problem between functions. In general, however, file scope objects complicate the independence and understandability of individual functions. An alternative solution, in this case, is a local static object. For example, const vector<int>* fibon_seq( int size ) { static vector< int > elems; // do the logic to populate it ... return &elems; } elems is now defined as a local static object of fibon_seq(). What does this mean? Unlike a nonstatic local object, the memory associated with a static local object persists across function invocations. elems is no longer destroyed and re-created with each invocation of fibon_seq(). This is why we can now safely return elems's address. A local static object allows us to define a single vector to hold the elements of the Fibonacci sequence. With each invocation of fibon_seq(), we need calculate only those elements that are not as yet inserted into elems. Here is one possible implementation: const vector<int>* fibon_seq( int size ) { const int max_size = 1024; static vector< int > elems; if ( size <= 0 || size > max_size ){ cerr << "fibon_seq(): oops: invalid size: " << size << " -- can't fulfill request.\n"; return 0; } // if size is equal to or greater than elems.size(), // no calculations are necessary ... for ( int ix = elems.size(); ix < size; ++ix ){ if ( ix == 0 || ix == 1 ) elems.push_back( 1 ); else elems.push_back( elems[ix-1]+elems[ix-2] ); } return &elems; } Previously, we have always defined a vector to be of a particular size and have assigned values to existing elements. But in this version of fibon_seq(), we have no way of guessing how big a vector we'll need. Rather, we define elems to be an empty vector and insert elements as we need them. push_back() inserts the value at the back of the vector. The memory to support this is managed automatically by the vector class itself. (In Chapter 3 we look in detail at vectors and the other standard library container classes.) |
I l@ve RuBoard |
![]() ![]() |