Tuesday, November 24
Statics

Topic

C++ style constants (often called constant variables, if that can make sense).

- A constant is just a variable that is set once and never changed.  From the computer's viewpoint, it handles variables and constants exactly the same way.
- The compiler can check if a variable is changed, and therefore enforce if that "variable" is really a constant at compile time.  If you declare a "variable" to be constant, then the compiler makes sure that it is not changed after the initialization.
- constants in C++ are just variables declared 
const.  They must be initialized in the declaration.  The compiler will enforce the initialization, and will give a compiler error if your code subsequently tries to change the value in the constant "variable".
- constant variables are much safer than
#defines and should usually be used instead of #defines

const double PI = 3.14159;


static variables and constants in classes

- As you know, if you declare an instance variable inside a class definition, there will be a separate variable for each object you create for that class.  Each class has its own set of instance variables that it controls.
- Unfortunately, this is also true if you declare a constant inside the class.  Even though the constant will be the same value for each object, each object will still have its own copy.  This is very space inefficient if you create a lot of objects for that class.
- Declaring a constant, or a variable, to be
static means there will be one and only one instance of that constant or variable, no matter how many objects are created.
- Each object will still think that it has a private copy of that constant or variable, but in reality all objects of that class will share the same single constant or variable.
- For example, for the silly 
class Blipple

class Blipple
{
private:
   const string SPECIES = "Blipple";
   string name;
public:
   Blipple(string n) name(n) {};
   string whatsyourspecies() {return SPECIES;};
   string whatsyourname() {return name;};
};

- Defined this way, every Blipple object will have its own
SPECIES string, "Blipple".  If there are 1000 Blipples, then there will be 1000 copies of the string.  That is very wasteful.   string name; however, will be different for each Blipple (hopefully), and 1000 different name strings, one per object, makes sense.
- Also, since SPECIES can never be changed, there is no reason to keep it
private.  Whether to make a constant private or public is a design decision, but either way is good programming.  If only objects of that class are going to use the constant, then keep it private.  If there is any reason that other parts of your code will need the constant's value, make it public.
- The problems can be solved with the following small changes:

class Blipple
{
private:
   string name;

public:
   const static string SPECIES = "Blipple";

   Blipple(string n) name(n) {};
   string whatsyourspecies() {return SPECIES;};
   string whatsyourname() {return name;);
};

- By adding
 static to the SPECIES declaration, it means that one and only one string will be stored in memory.  Each object can refer to it, and each object thinks that it is its "own" constant, but all Blipples will really be sharing the same string.  Since it cannot be changed, there is no problem with multiple objects sharing it.
- However there is a problem with the initialization.  Theoretically, it will be initialized each time an object is created.  This is a problem because a constant can only be initialized once.  The above code will not compile.
- Instead, you don't initialize a static variable inside the class.  You initialize it once before the program starts.  This is done by putting what is essentially a duplicate global definition before the
int main().   You could also put it in a header file, .h, but it must be read before the compiler gets to  int main().
- Here is the corrected syntax:

class Blipple
{
private:
   string name;

public:
   const static string SPECIES;

   Blipple(string n) name(n) {};
   string whatsyourspecies() {return SPECIES;};
   string whatsyourname() {return name;);
};

// in .h or .cpp before int main()
const string Blipple::SPECIES = "Blipple"; 
int main()
{
// and so on...

- This syntax has been criticized because there are two declarations, even though only one variable is actually created.  Notice also that the global definitions of  const string Blipple::SPECIES;  does not contain the keyword static  but the declaration inside the class, const static string SPECIES;   does.  C++ syntax only allows static used inside a class declaration.  It cannot be used outside the class definition, even for the initializations for that class's static variables. 

- If you use
static  for a variable, all the concepts are the same, except you are now allowed to change the static variable.  This means that any object of that class will use the static variable as if it were its own private instance variable, but if one object changes it, all the other objects will see the change in their "own" variable.  This allows sharing of information among all objects of a class.
- As an example:
 

class Blipple
{
private:
   string name;
   static int numberofblipples;

public:
   const static string SPECIES;

   Blipple(string n) name(n) {numberofblipples++;};
   ~Blipple() {numberofblipples--;};
   string whatsyourspecies() {return SPECIES;};
   string whatsyourname() {return name;);
   int howmanyblipples() {return numberofblipples;);
};

// in .h or .cpp before int main()
const string Blipple::SPECIES = "Blipple";
int Blipple::numberofblipples = 0; 
int main()
{
// and so on...

- Notice that when each Blipple is constructed numberofblipples; incremented.  Since all these Blipple constructors are incrementing the same single variable,  numberofblipples will keep track of how many Blipples have been created in total.  Also the Blipple destructor will ensure that numberofblipples  always contains how many active Blipples are executing at any time. 
- Each Blipple can refer to it "own" copy of 
numberofblipples  to know how many Blipples are active.  The value is effectively distributed to all Blipples because the variable is static.
- In addition, if no Blipples have been created yet,
numberofblipples will still exist, with its initial value of 0, because  numberofblipples is the same as a global variable, except it belongs to class Blipple

- If a constant is public and static, then it can be used in any code by putting the class name before the dot, not necessarily an object name of that class.  The static constant is accessible independently of any object.

- If you want, you can still access the static constant using an object's name.  It does not matter which object's name you use, because you will get the same value from any object of that class at any given time in your program's execution.

Blipple jian("Jian"), lisa("Lisa"), henry{"Henry");
   // later in your code
cout << "Lisa is a " << lisa.SPECIES << endl;


static functions

- In addition, member functions inside a class can be declared
static if the function does not access or change any non-static instance variables.  In other words, static functions can only use static instance variables, or no instance variables at all.

class Blipple
{
private:
   string name;
   static int numberofblipples;

public:
   const static string SPECIES;

   Blipple(string n) name(n) {numberofblipples++;};
   ~Blipple() {numberofblipples--;};
   static string whatsyourspecies() {return SPECIES;};
   string whatsyourname() {return name;);
   static int howmanyblipples() {return numberofblipples;);
};



- In the above Blipple definition,
howmanyblipples() and whatsyourspecies meet that definition, but whatsyourname() does not.  Therefore, anywhere in your code you can get the SPECIES or numberofblipples by using the static function calls:

// no change
cout << "There are now " << Blipple.howmanyblipples() << " Blipples." << endl;
cout << "Jain is a " << Blipple.whatsyourspecies() << "." << endl;

- The second function is reduntent, however, because  SPECIES is public.  You could also write:

cout << "Jain is a " << Blipple.SPECIES << "." << endl;

or

cout << "Jain is a " << jian.SPECIES << "." << endl;


- Whenever a function accesses only static instance variables or no instance variables at all, it should be declared static.  This allows the users to call the function using the ClassName.  syntax, even if there is no object of that class in scope at that point in the code.

static functions calling other functions

- A static function can only call another static function.  This should make sense, because if a static function could call a non-static function, then that non-static might change a non-static instance variable, which is forbidden for a static function.
- The mnemonic is "If static, only static".
- Non-static functions can call any function within the class, static or non-static, and access any constant or variable, static or non-static.  A non-static function is allowed to change any instance variables, so it does not matter whether the functions it calls changes static variables or non-static variables.
- The mnemonic is "If not static, anything".



Readings

Deitel and Deitel Fifth Edition, Chapter ?.?, ?.?, ?.?

 

Back to Csc 125 Programming in C++
  Scott Badman  Office:  B132  Phone:  353-2250  sbadman@parkland.edu  

Parkland College, 2400 W. Bradley Avenue, Champaign, IL 61821