Wednesday, February 24, 2010

Static Data Members and Static Member Functions

The static storage class specifier

Objects declared with the static storage class specifier have static storage duration, which means that memory for these objects is allocated when the program begins running and is freed when the program terminates. Static storage duration for a variable is different from file or global scope: a variable can have static duration but local scope.

The keyword static is the major mechanism in C to enforce information hiding.

C++ enforces information hiding through the namespace language feature and the access control of classes. The use of the keyword static to limit the scope of external variables is deprecated for declaring objects in namespace scope.

The static storage class specifier can be applied to the following declarations:

You cannot use the static storage class specifier with the following:

Linkage of static variables

A declaration of an object that contains the static storage class specifier and has file scope gives the identifier internal linkage. Each instance of the particular identifier therefore represents the same object within one file only. For example, if a static variable x has been declared in function f, when the program exits the scope of f, x is not destroyed:

#include <stdio.h>


 

int f(void) {

  static int x = 0;

  x++;

  return x;

}


 

int main(void) {

  int j;

  for (j = 0; j < 5; j++) {

    printf("Value of f(): %d\n", f());

  }

  return 0;

}

The following is the output of the above example:

Value of f(): 1

Value of f(): 2

Value of f(): 3

Value of f(): 4

Value of f(): 5

Because x is a static variable, it is not reinitialized to 0 on successive calls to f.


 


 

Static members

Class members can be declared using the storage class specifier static in the class member list. Only one copy of the static member is shared by all objects of a class in a program. When you declare an object of a class having a static member, the static member is not part of the class object.

A typical use of static members is for recording data common to all objects of a class. For example, you can use a static data member as a counter to store the number of objects of a particular class type that are created. Each time a new object is created, this static data member can be incremented to keep track of the total number of objects.

You access a static member by qualifying the class name using the :: (scope resolution) operator. In the following example, you can refer to the static member f() of class type X as X::f() even if no object of type X is ever declared:

struct X {

  static int f();

};


 

int main() {

  X::f();

}


 

Using the class access operators with static members

You do not have to use the class member access syntax to refer to a static member; to access a static member s of class X, you could use the expression X::s. The following example demonstrates accessing a static member:

#include <iostream>

using namespace std;


 

struct A {

  static void f() { cout << "In static function A::f()" << endl; }

};


 

int main() {


 

  // no object required for static member

  A::f();


 

  A a;

  A* ap = &a;

  a.f();

  ap->f();

}

The three statements A::f(), a.f(), and ap->f() all call the same static member function A::f().

You can directly refer to a static member in the same scope of its class, or in the scope of a class derived from the static member's class. The following example demonstrates the latter case (directly referring to a static member in the scope of a class derived from the static member's class):

#include <iostream>

using namespace std;


 

int g() {

   cout << "In function g()" << endl;

   return 0;

}


 

class X {

   public:

      static int g() {

         cout << "In static member function X::g()" << endl;

         return 1;

      }

};


 

class Y: public X {

   public:

      static int i;

};


 

int Y::i = g();


 

int main() { }

The following is the output of the above code:

In static member function X::g()

The initialization int Y::i = g() calls X::g(), not the function g() declared in the global namespace.


 


 

Static data members

The declaration of a static data member in the member list of a class is not a definition. You must define the static member outside of the class declaration, in namespace scope. For example:

class X

{

public:

static int i;

};

int X::i = 0; // definition outside class declaration

Once you define a static data member, it exists even though no objects of the static data member's class exist. In the above example, no objects of class X exist even though the static data member X::i has been defined.

Static data members of a class in namespace scope have external linkage. The initializer for a static data member is in the scope of the class declaring the member.

A static data member can be of any type except for void or void qualified with const or volatile. You cannot declare a static data member as mutable.

You can only have one definition of a static member in a program. Unnamed classes, classes contained within unnamed classes, and local classes cannot have static data members.

Static data members and their initializers can access other static private and protected members of their class. The following example shows how you can initialize static members using other static members, even though these members are private:

class C {

static int i;

static int j;

static int k;

static int l;

static int m;

static int n;

static int p;

static int q;

static int r;

static int s;

static int f() { return 0; }

int a;

public:

C() { a = 0; }

};

C c;

int C::i = C::f(); // initialize with static member function

int C::j = C::i; // initialize with another static data member

int C::k = c.f(); // initialize with member function from an object

int C::l = c.j; // initialize with data member from an object

int C::s = c.a; // initialize with nonstatic data member

int C::r = 1; // initialize with a constant value

class Y : private C {} y;


 

int C::m = Y::f();

int C::n = Y::r;

int C::p = y.r; // error

int C::q = y.f(); // error

The initializations of C::p and C::q cause errors because y is an object of a class that is derived privately from C, and its members are not accessible to members of C.

If a static data member is of const integral or const enumeration type, you may specify a constant initializer in the static data member's declaration. This constant initializer must be an integral constant expression. Note that the constant initializer is not a definition. You still need to define the static member in an enclosing namespace. The following example demonstrates this:

#include <iostream>

using namespace std;


 

struct X {

static const int a = 76;

};


 

const int X::a;


 

int main() {

cout << X::a << endl;

}

The tokens = 76 at the end of the declaration of static data member a is a constant initializer.

Static member functions

You cannot have static and nonstatic member functions with the same names and the same number and type of arguments.

Like static data members, you may access a static member function f() of a class A without using an object of class A.

A static member function does not have a this pointer. The following example demonstrates this:

#include <iostream>

using namespace std;


 

struct X {

private:

  int i;

  static int si;

public:

  void set_i(int arg) { i = arg; }

  static void set_si(int arg) { si = arg; }


 

  void print_i() {

    cout << "Value of i = " << i << endl;

    cout << "Again, value of i = " << this->i << endl;

  }


 

  static void print_si() {

    cout << "Value of si = " << si << endl;

//    cout << "Again, value of si = " << this->si << endl;

  }


 

};


 

int X::si = 77;       // Initialize static data member


 

int main() {

  X xobj;

  xobj.set_i(11);

  xobj.print_i();


 

  // static data members and functions belong to the class and

  // can be accessed without using an object of class X

  X::print_si();

  X::set_si(22);

  X::print_si();

}

The following is the output of the above example:

Value of i = 11

Again, value of i = 11

Value of si = 77

Value of si = 22

The compiler does not allow the member access operation this->si in function A::print_si() because this member function has been declared as static, and therefore does not have a this pointer.

You can call a static member function using the this pointer of a nonstatic member function. In the following example, the nonstatic member function printall() calls the static member function f() using the this pointer:

#include <iostream>

using namespace std;


 

class C {

  static void f() {

    cout << "Here is i: " << i << endl;

  }

  static int i;

  int j;

public:

  C(int firstj): j(firstj) { }

  void printall();

};


 

void C::printall() {

  cout << "Here is j: " << this->j << endl;

  this->f();

}


 

int C::i = 3;


 

int main() {

  C obj_C(0);

  obj_C.printall();

}

The following is the output of the above example:

Here is j: 0

Here is i: 3

A static member function cannot be declared with the keywords virtual, const, volatile, or const volatile.

A static member function can access only the names of static members, enumerators, and nested types of the class in which it is declared. Suppose a static member function f() is a member of class X. The static member function f() cannot access the nonstatic members X or the nonstatic members of a base class of X.

No comments:

Post a Comment