I l@ve RuBoard |
![]() ![]() |
4.5 Static Class MembersIn our procedural implementation of Chapter 2, we maintain one container instance to hold the elements of the Fibonacci sequence through use of a local static vector. Our class implementation also needs only one container instance to hold the elements of each numeric sequence. The static keyword again provides the solution, although it means something different when used within a class. A static data member represents a single, shared instance of that member that is accessible to all the objects of that class. In the following class definition, for example, we declare _elems as a static data member of the Triangular class: class Triangular { public: // ... private: static vector<int> _elems; }; Because only a single instance of a static data member exists, we must provide an explicit definition of that instance within a program text file. The definition looks like the global definition of an object except that its name is qualified with the class scope operator: // placed in program text file, such as Triangular.cpp vector<int> Triangular::_elems; An initial value, if desired, can also be specified: int Triangular::_initial_size = 8; The member functions of the class can access a static data member the same as if it were an ordinary data member: Triangular::Triangular( int len, int beg_pos ) : _length( len > 0 ? len : 1 ), _beg_pos( beg_pos > 0 ? beg_pos : 1 ) { _next = _beg_pos-1; int elem_cnt = _beg_pos + _length; if ( _elems.size() < elem_cnt ) gen_elements( elem_cnt ); } A const static int data member, such as buf_size, next, is the one instance in which a class member can be explicitly initialized within its declaration: class intBuffer { public: // ... private: static const int _buf_size = 1024; // ok int _buffer[ _buf_size ]; // ok }; Static Member FunctionsConsider the following implementation of is_elem(). Given a value, it returns true or false depending on whether the value is an element of the Triangular sequence: bool Triangular:: is_elem( int value ) { if ( ! _elems.size() || _elems[ _elems.size()-1 ] < value ) gen_elems_to_value( value ); vector<int>::iterator found_it; vector<int>::iterator end_it = _elems.end(); found_it = find( _elems.begin(), end_it, value ); return found_it != end_it; } Ordinarily, a member function must be invoked through an object of the class. The object is bound to the member function's this pointer. It is through the this pointer that a member function accesses the nonstatic data members stored within each class object. is_elem(), however, does not access any nonstatic data members. Its operation is independent of any particular class object, and it would be convenient to invoke it as a freestanding function. We can't write if ( is_elem( 8 )) ... however, because there is no way for the compiler or reader to know which is_elem() we want to invoke. Use of the class scope operator clears up that ambiguity: if ( Triangular::is_elem( 8 )) ... A static member function can be invoked independently of a class object in exactly this way. A member function can be declared as static only if it does not access any nonstatic class members. We make it static by prefacing its declaration within the class definition with the keyword static: class Triangular { public: static bool is_elem( int ); static void gen_elements( int length ); static void gen_elems_to_value( int value ); static void display( int len, int beg_pos, ostream &os = cout ); // ... private: static const int _max_elems = 1024; static vector<int> _elems; // ... }; When defined outside the class body, the static keyword is not repeated (this is also true of static data members): void Triangular:: gen_elems_to_value( int value ) { int ix = _elems.size(); if ( ! ix ) { _elems.push_back( 1 ); ix = 1; } while ( _elems[ ix-1 ] < value && ix < _max_elems ) { // cout << "elems to value: " << ix*(ix+1)/2 << endl; _elems.push_back( ix*(ix+1)/2 ); ++ix; } if ( ix == _max_elems ) cerr << "Triangular Sequence: oops: value too large " << value << " -- exceeds max size of " << _max_elems << endl; } Here is an example of how we might independently invoke is_elem(): #include <iostream> #include "Triangular.h" using namespace std; int main() { char ch; bool more = true; while ( more ) { cout << "Enter value: "; int ival; cin >> ival; bool is_elem = Triangular::is_elem( ival ); cout << ival << ( is_elem ? " is " : " is not " ) << "an element in the Triangular series.\n" << "Another value? (y/n) "; cin >> ch; if ( ch == 'n' || ch == 'N') more = false; } } When the program is compiled and executed, it generates the following output (my input is highlighted in bold): Enter value: 1024 1024 is not an element in the Triangular series. Another value? (y/n) y Enter value: 0 0 is not an element in the Triangular series. Another value? (y/n) y Enter value: 36 36 is an element in the Triangular series. Another value? (y/n) y Enter value: 55 55 is an element in the Triangular series. Another value? (y/n) n For completeness, here is the definition of gen_elements(): void Triangular:: gen_elements( int length ) { if ( length < 0 || length > _max_elems ){ // issue error message and return } if ( _elems.size() < length ) { int ix = _elems.size() ? _elems.size()+1 : 1; for ( ; ix <= length-1; ++ix ) _elems.push_back( ix*(ix+1)/2 ); } } |
I l@ve RuBoard |
![]() ![]() |