Tuesday 28 March 2017

Teach Yourself C++ In 21 Days Second Edition.Day 14

Day 14


Special Classes and Functions

C++ offers a number of ways to limit the scope and impact of variables and pointers. So far you've seen how to create global variables, local function variables, pointers to variables, and class member variables. Today you learn

       What static member variables and static member functions are.

       How to use static member variables and static member functions.

       How to create and manipulate pointers to functions and pointers to member functions.

       How to work with arrays of pointers to functions.

Static Member Data


Until now, you have probably thought of the data in each object as unique to that object and not shared among objects in a class. For example, if you have five Cat objects, each has its own age, weight, and other data. The age of one does not affect the age of another.


There are times, however, when you'll want to keep track of a pool of data. For example, you might want to know how many objects for a specific class have been created in your program, and how many are still in existence. Static member variables are shared among all instances of a class. They are a compromise between global data, which is available to all parts of your program, and member data, which is usually available only to each object.

You can think of a static member as belonging to the class rather than to the object. Normal member

data is one per object, but static members are one per class. Listing 14.1 declares a Cat object with a static data member, HowManyCats. This variable keeps track of how many Cat objects have been created. This is done by incrementing the static variable, HowManyCats, with each construction and
decrementing it with each destruction.

Listing 14.1. Static member data.

1:                  //Listing 14.1 static data members

2:

3: #include <iostream.h> 4:

     class Cat

     {

     public:

     Cat(int age):itsAge(age){HowManyCats++; }

     virtual ~Cat() { HowManyCats--; }

     virtual int GetAge() { return itsAge; }

     virtual void SetAge(int age) { itsAge = age; }

12:                          static int HowManyCats;

13:

     private:

     int itsAge;



17:               };

18:

19: int Cat::HowManyCats = 0; 20:

     int main()

     {

     const int MaxCats = 5; int i;

     Cat *CatHouse[MaxCats];

     for (i = 0; i<MaxCats; i++)

26:                                     CatHouse[i] = new Cat(i);

27:

     for (i = 0; i<MaxCats; i++)

     {

30:                                     cout << "There are ";

31:                                     cout << Cat::HowManyCats;

32:                                     cout << " cats left!\n";

33:                                     cout << "Deleting the one which is ";

34: cout << CatHouse[i]->GetAge(); 35: cout << " years old\n";

36:                                     delete CatHouse[i];

37:                                     CatHouse[i] = 0;

     }

     return 0;

     }

Output: There are 5 cats left!

Deleting the one
which is 0 years old
There are 4
cats
left!
Deleting the one
which is 1 years old
There are 3
cats
left!
Deleting the one
which is 2 years old
There are 2
cats
left!
Deleting the one
which is 3 years old
There are 1
cats
left!
Deleting the one
which is 4 years old

Analysis: On lines 5 to 17 the simplified class Cat is declared. On line 12, HowManyCats is declared to be a static member variable of type int.

The declaration of HowManyCats does not define an integer; no storage space is set aside. Unlike the non-static member variables, no storage space is set aside by instantiating a Cat object, because

the HowManyCats member variable is not in the object. Thus, on line 19 the variable is defined and initialized.


It is a common mistake to forget to define the static member variables of classes. Don't let this happen to you! Of course, if it does, the linker will catch it with a pithy error message such as the following:

undefined symbol Cat::HowManyCats

You don't need to do this for itsAge, because it is a non-static member variable and is defined each time you make a Cat object, which you do here on line 26.

The constructor for Cat increments the static member variable on line 8. The destructor decrements it on line 9. Thus, at any moment, HowManyCats has an accurate measure of how many Cat objects
were created but not yet destroyed.

The driver program on lines 21-40 instantiates five Cats and puts them in an array. This calls five Cat constructors, and thus HowManyCats is incremented five times from its initial value of 0.

The program then loops through each of the five positions in the array and prints out the value of HowManyCats before deleting the current Cat pointer. The printout reflects that the starting value is

(after all, 5 are constructed), and that each time the loop is run, one fewer Cat remains.

Note that HowManyCats is public and is accessed directly by main(). There is no reason to expose this member variable in this way. It is preferable to make it private along with the other member variables and provide a public accessor method, as long as you will always access the data through an instance of Cat. On the other hand, if you'd like to access this data directly without necessarily having a Cat object available, you have two options: keep it public, as shown in Listing 14.2, or provide a static member function, as discussed later in this chapter.

Listing 14.2. Accessing static members without an object.


1: //Listing 14.2 static data members 2:

3:                  #include <iostream.h>

4:

     class Cat

     {

     public:

     Cat(int age):itsAge(age){HowManyCats++; }

     virtual ~Cat() { HowManyCats--; }

     virtual int GetAge() { return itsAge; }

     virtual void SetAge(int age) { itsAge = age; }

     static int HowManyCats;


13:

     private:

15:                          int itsAge;

16:

17: }; 18:

19: int Cat::HowManyCats = 0; 20:

21:               void TelepathicFunction();

22:

     int main()

     {

     const int MaxCats = 5; int i;

     Cat *CatHouse[MaxCats];

     for (i = 0; i<MaxCats; i++)

     {

29: CatHouse[i] = new Cat(i); 30: TelepathicFunction();

31:                          }

32:

     for ( i = 0; i<MaxCats; i++)

     {

35: delete CatHouse[i]; 36: TelepathicFunction();

     }

     return 0;

     }

40:

     void TelepathicFunction()

     {

     cout << "There are ";

     cout << Cat::HowManyCats << " cats alive!\n";

     }

Output: There are 1 cats alive!

There are 2 cats alive!

There are 3 cats alive!

There are 4 cats alive!

There are 5 cats alive!

There are 4 cats alive!

There are 3 cats alive!

There are 2 cats alive!

There are 1 cats alive!

There are 0 cats alive!


Analysis: Listing 14.2 is much like Listing 14.1 except for the addition of a new function, TelepathicFunction(). This function does not create a Cat object, nor does it take a Cat object as a parameter, yet it can access the HowManyCats member variable. Again, it is worth

reemphasizing that this member variable is not in any particular object; it is in the class as a whole, and, if public, can be accessed by any function in the program.


The alternative to making this member variable public is to make it private. If you do, you can access it through a member function, but then you must have an object of that class available. Listing 14.3 shows this approach. The alternative, static member functions, is discussed immediately after the analysis of Listing 14.3.

Listing 14.3. Accessing static members using non-static member functions.


1:                  //Listing 14.3 private static data members

2:

3: #include <iostream.h> 4:

     class Cat

     {

     public:

     Cat(int age):itsAge(age){HowManyCats++; }

     virtual ~Cat() { HowManyCats--; }

     virtual int GetAge() { return itsAge; }

     virtual void SetAge(int age) { itsAge = age; }

     virtual int GetHowMany() { return HowManyCats; }





     private:

     int itsAge;

     static int HowManyCats;

     };

19:

20:               int Cat::HowManyCats = 0;

21:

     int main()

     {

     const int MaxCats = 5; int i;

     Cat *CatHouse[MaxCats];

     for (i = 0; i<MaxCats; i++)

27: CatHouse[i] = new Cat(i); 28:

     for (i = 0; i<MaxCats; i++)

     {

31:                                     cout << "There are ";

32: cout << CatHouse[i]->GetHowMany(); 33: cout << " cats left!\n";

34: cout << "Deleting the one which is "; 35: cout << CatHouse[i]->GetAge()+2;

36:                                     cout << " years old\n";

37: delete CatHouse[i]; 38: CatHouse[i] = 0;

     }

     return 0;

     }

Output: There are 5 cats left!

Deleting the one
which is 2 years old
There are 4
cats
left!
Deleting the one
which is 3 years old
There are 3
cats
left!
Deleting the one
which is 4 years old
There are 2
cats
left!
Deleting the one
which is 5 years old
There are 1
cats
left!
Deleting the one
which is 6 years old

Analysis: On line 17, the static member variable HowManyCats is declared to have private access. Now you cannot access this variable from non-member functions, such as TelepathicFunction
from the previous listing.

Even though HowManyCats is static, it is still within the scope of the class. Any class function, such as GetHowMany(), can access it, just as member functions can access any member data. However, for a function to call GetHowMany(), it must have an object on which to call the function.


DO use static member variables to share data among all instances of a class. DO make static member variables protected or private if you wish to restrict access to them. DON'T use static member variables to store data for one object. Static member data is shared among all objects of its class.


Static Member Functions


Static member functions are like static member variables: they exist not in an object but in the scope of the class. Thus, they can be called without having an object of that class, as illustrated in Listing 14.4.

Listing 14.4. Static member functions.


1:                  //Listing 14.4 static data members

2:

3: #include <iostream.h> 4:

     class Cat

     {

     public:

     Cat(int age):itsAge(age){HowManyCats++; }

     virtual ~Cat() { HowManyCats--; }

     virtual int GetAge() { return itsAge; }

     virtual void SetAge(int age) { itsAge = age; }

     static int GetHowMany() { return HowManyCats; }

     private:

     int itsAge;

     static int HowManyCats;

     };

17:

18: int Cat::HowManyCats = 0; 19:

20:               void TelepathicFunction();

21:

     int main()

     {

     const int MaxCats = 5;

     Cat *CatHouse[MaxCats]; int i;

     for (i = 0; i<MaxCats; i++)

     {

28:                                     CatHouse[i] = new Cat(i);

29:                                     TelepathicFunction();

30:                          }

31:

     for ( i = 0; i<MaxCats; i++)

     {

34:                                     delete CatHouse[i];

35:                                     TelepathicFunction();

     }

     return 0;

     }

39:

     void TelepathicFunction()

     {

     cout << "There are " << Cat::GetHowMany() << " cats

alive!\n";

     }

Output: There are 1 cats alive!

There are 2 cats alive!

There are 3 cats alive!

There are 4 cats alive!

There are 5 cats alive!

There are 4 cats alive!

There are 3 cats alive!

There are 2 cats alive!

There are 1 cats alive!

There are 0 cats alive!

Analysis: The static member variable HowManyCats is declared to have private access on line 15 of the Cat declaration. The public accessor function, GetHowMany(), is declared to be both public
and static on line 12.

Since GetHowMany() is public, it can be accessed by any function, and since it is static there is no
need to have an object of type Cat on which to call it. Thus, on line 42, the function TelepathicFunction() is able to access the public static accessor, even though it has no access to a Cat object. Of course, you could have called GetHowMany() on the Cat objects available in main(), just as with any other accessor functions.


NOTE: Static member functions do not have a this pointer. Therefore, they cannot be declared const. Also, because member data variables are accessed in member functions using the this pointer, static member functions cannot access any non-static member variables!


Static Member Functions


You can access static member functions by calling them on an object of the class just as you do any other member function, or you can call them without an object by fully qualifying the class and object name. Example

class Cat

{

public:

static int GetHowMany() { return HowManyCats; } private:

static int HowManyCats;

};

int Cat::HowManyCats = 0;

int main()

{

int howMany;

Cat theCat;                                                                                      // define a cat

howMany = theCat.GetHowMany();             // access through an object

howMany = Cat::GetHowMany();                    // access without an object

}

Pointers to Functions


Just as an array name is a constant pointer to the first element of the array, a function name is a constant pointer to the function. It is possible to declare a pointer variable that points to a function, and to invoke the function by using that pointer. This can be very useful; it allows you to create programs that decide which functions to invoke based on user input.

The only tricky part about function pointers is understanding the type of the object being pointed to. A pointer to int points to an integer variable, and a pointer to a function must point to a function of the appropriate return type and signature.

In the declaration

long (* funcPtr) (int);

funcPtr is declared to be a pointer (note the * in front of the name) that points to a function that takes an integer parameter and returns a long. The parentheses around * funcPtr are necessary

because the parentheses around int bind more tightly, that is they have higher precedence than the indirection operator (*). Without the first parentheses this would declare a function that takes an integer and returns a pointer to a long. (Remember that spaces are meaningless here.)

Examine these two declarations:

long * Function (int); long (* funcPtr) (int);

The first, Function (), is a function taking an integer and returning a pointer to a variable of type long. The second, funcPtr, is a pointer to a function taking an integer and returning a variable of type long.

The declaration of a function pointer will always include the return type and the parentheses indicating the type of the parameters, if any. Listing 14.5 illustrates the declaration and use of function pointers.

Listing 14.5. Pointers to functions.

1:                  // Listing 14.5 Using function pointers

2:

3: #include <iostream.h> 4:

     void Square (int&,int&);

     void Cube (int&, int&);

     void Swap (int&, int &);

     void GetVals(int&, int&);

     void PrintVals(int, int);

     enum BOOL { FALSE, TRUE };



     int main()

     {

     void (* pFunc) (int &, int &);

     BOOL fQuit = FALSE;

16:

     int valOne=1, valTwo=2;

     int choice;

     while (fQuit == FALSE)

     {

21:
cout << "(0)Quit (1)Change Values (2)Square (3)Cube
(4)Swap: ";

22:
cin >> choice;
23:
switch (choice)
24:
{
25:
case 1: pFunc = GetVals; break;
26:
case 2: pFunc = Square; break;
27:
case 3: pFunc = Cube; break;
28:
case 4: pFunc = Swap; break;
29:
default : fQuit = TRUE; break;
30:
}
31:

32:
if (fQuit)
33:
break;
34:

35:
PrintVals(valOne, valTwo);
36:
pFunc(valOne, valTwo);
37:
PrintVals(valOne, valTwo);
     }

     return 0;

     }

41:

     void PrintVals(int x, int y)

     {

     cout << "x: " << x << " y: " << y << endl;

     }

46:

     void Square (int & rX, int & rY)

     {

     rX *= rX;

     rY *= rY;

     }

52:

53:               void Cube (int & rX, int & rY)

     {

     int tmp;



     tmp = rX;

     rX *= rX;

     rX = rX * tmp;



     tmp = rY;

     rY *= rY;

     rY = rY * tmp;

     }

65:

     void Swap(int & rX, int & rY)

     {

     int temp;

     temp = rX;

     rX = rY;

     rY = temp;

     }

73:

     void GetVals (int & rValOne, int & rValTwo)

     {

     cout << "New value for ValOne: ";

     cin >> rValOne;

     cout << "New value for ValTwo: ";

     cin >> rValTwo;

     }

Output: (0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 1 x: 1 y: 2

New value for ValOne: 2

New value for ValTwo: 3 x: 2 y: 3

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 3 x: 2 y: 3

x: 8 y: 27

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 2 x: 8 y: 27

x: 64 y: 729

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 4 x: 64 y: 729

x: 729 y: 64

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 0


Analysis: On lines 5-8, four functions are declared, each with the same return type and signature, returning void and taking two references to integers.

On line 14, pFunc is declared to be a pointer to a function that returns void and takes two integer reference parameters. Any of the previous functions can be pointed to by pFunc. The user is

repeatedly offered the choice of which functions to invoke, and pFunc is assigned accordingly. On lines 35-36, the current value of the two integers is printed, the currently assigned function is invoked, and then the values are printed again.

Pointer to Function


A pointer to function is invoked exactly like the functions it points to, except that the function pointer name is used instead of the function name. Assign a pointer to function to a specific function by assigning to the function name without the parentheses. The function name is a constant pointer to the function itself. Use the pointer to function just as you would the function name. The pointer to function must agree in return value and signature with the function to which you assign it. Example

long (*pFuncOne) (int, int); long SomeFunction (int, int); pFuncOne = SomeFunction; pFuncOne(5,7);

Why Use Function Pointers?


You certainly could write the program in Listing 14.5 without function pointers, but the use of these pointers makes the intent and use of the program explicit: pick a function from a list, and then invoke it.

Listing 14.6 uses the function prototypes and definitions from Listing 14.5, but the body of the program does not use a function pointer. Examine the differences between these two listings.


NOTE: To compile this program, place lines 41-80 from Listing 14.5 immediately after line 56.


Listing 14.6. Rewriting Listing 14.5 without the pointer to function.

1: // Listing 14.6 Without function pointers 2:

3: #include <iostream.h> 4:

     void Square (int&,int&);

     void Cube (int&, int&);

     void Swap (int&, int &);

     void GetVals(int&, int&);

     void PrintVals(int, int);

10:               enum BOOL { FALSE, TRUE };

11:

     int main()

     {

     BOOL fQuit = FALSE;

     int valOne=1, valTwo=2;

     int choice;

     while (fQuit == FALSE)

     {

19:
cout << "(0)Quit (1)Change Values (2)Square (3)Cube
(4)Swap: ";
20:
cin >> choice;
21:
switch (choice)
22:
{
23:
case 1:
24:
PrintVals(valOne, valTwo);
25:
GetVals(valOne, valTwo);
26:
PrintVals(valOne, valTwo);
27:
break;
28:

29:
case 2:
30:
PrintVals(valOne, valTwo);
31:
Square(valOne,valTwo);
32:
PrintVals(valOne, valTwo);
33:
break;
34:

35:
case 3:
36:
PrintVals(valOne, valTwo);
37:
Cube(valOne, valTwo);
38:
PrintVals(valOne, valTwo);
39:
break;
40:

41:
case 4:
42:
PrintVals(valOne, valTwo);
43:
Swap(valOne, valTwo);
44:
PrintVals(valOne, valTwo);
45:
break;
46:

47:
default :
48:
fQuit = TRUE;
49:
break;
50:
}
51:

52:
if (fQuit)
53:
break;
54:
}

     return 0;

     }

Output: (0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 1 x: 1 y: 2

New value for ValOne: 2

New value for ValTwo: 3

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 3 x: 2 y: 3

x: 8 y: 27

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 2 x: 8 y: 27

x: 64 y: 729

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 4 x: 64 y: 729

x: 729 y: 64

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 0


Analysis: The implementation of the functions has been left out, because it is identical to that

provided in Listing 14.5. As you can see, the output is unchanged, but the body of the program has expanded from 27 lines to 38. The calls to PrintVals() must be repeated for each case.

It was tempting to put PrintVals() at the top of the while loop and again at the bottom, rather than in each case statement. This would have called PrintVals() even for the exit case, however,
and that was not part of the specification.


Setting aside the increased size of the code and the repeated calls to do the same thing, the overall clarity is somewhat diminished. This is an artificial case, however, created to show how pointers to functions work. In real-world conditions the advantages are even clearer: pointers to functions can eliminate duplicate code, clarify your program, and allow you to make tables of functions to call based on runtime conditions.

Shorthand Invocation


The pointer to function does not need to be dereferenced, though you are free to do so. Therefore, if pFunc is a pointer to a function taking an integer and returning a variable of type long, and you

assign pFunc to a matching function, you can invoke that function with either

pFunc(x);


or

(*pFunc)(x);



The two forms are identical. The former is just a shorthand version of the latter.

Arrays of Pointers to Functions


Just as you can declare an array of pointers to integers, you can declare an array of pointers to functions returning a specific value type and with a specific signature. Listing 14.7 again rewrites Listing 14.5, this time using an array to invoke all the choices at once.


NOTE: To compile this program, place lines 41-80 of Listing 14.5 immediately after line 39.


Listing 14.7. Demonstrates use of an array of pointers to functions.

1: // Listing 14.7 demonstrates use of an array of pointers to functions

2:

3:                  #include <iostream.h>

4:

     void Square (int&,int&);

     void Cube (int&, int&);

     void Swap (int&, int &);

     void GetVals(int&, int&);

     void PrintVals(int, int);

     enum BOOL { FALSE, TRUE };



     int main()

     {

     int valOne=1, valTwo=2;

     int choice, i;

     const MaxArray = 5;

     void (*pFuncArray[MaxArray])(int&, int&);



     for (i=0;i<MaxArray;i++)

     {

21:
cout << "(1)Change Values (2)Square (3)Cube (4)Swap:
";

22:
cin >> choice;
23:
switch (choice)
24:
{
25:
case 1:pFuncArray[i] = GetVals; break;
26:
case 2:pFuncArray[i] = Square; break;
27:
case 3:pFuncArray[i] = Cube; break;
28:
case 4:pFuncArray[i] = Swap; break;
29:
default:pFuncArray[i] = 0;

30:
}
31:
}
32:

     for (i=0;i<MaxArray; i++)

     {

35:
pFuncArray[i](valOne,valTwo);
36:
PrintVals(valOne,valTwo);
     }

     return 0;

     }

Output: (1)Change Values (2)Square (3)Cube (4)Swap: 1

(1)Change Values (2)Square (3)Cube (4)Swap: 2 (1)Change Values (2)Square (3)Cube (4)Swap: 3 (1)Change Values (2)Square (3)Cube (4)Swap: 4 (1)Change Values (2)Square (3)Cube (4)Swap: 2

New Value for ValOne: 2

New Value for ValTwo: 3 x: 2 y: 3

x: 4 y: 9

x: 64 y: 729 x: 729 y: 64 x: 7153 y:4096

Analysis: Once again the implementation of the functions has been left out to save space, but it is the same as in Listing 14.5. On line 17, the array pFuncArray is de- clared to be an array of 5 pointers

to functions that return void and that take two integer references.

On lines 19-31, the user is asked to pick the functions to invoke, and each member of the array is assigned the address of the appropriate function. On lines 33-37, each function is invoked in turn. The result is printed after each invocation.

Passing Pointers to Functions to Other Functions


The pointers to functions (and arrays of pointers to functions, for that matter) can be passed to other functions, which may take action and then call the right function using the pointer.


For example, you might improve Listing 14.5 by passing the chosen function pointer to another function (outside of main()), which prints the values, invokes the function, and then prints the
values again. Listing 14.8 illustrates this variation.


WARNING: To compile this program, place lines 46-80 of Listing 14.5 immediately after line 45.

Listing 14.8. Passing pointers to functions as function arguments.

1: // Listing 14.8 Without function pointers 2:

3: #include <iostream.h> 4:

     void Square (int&,int&);

     void Cube (int&, int&);

     void Swap (int&, int &);

     void GetVals(int&, int&);

     void PrintVals(void (*)(int&, int&),int&, int&);

     enum BOOL { FALSE, TRUE };

11:

     int main()

     {

     int valOne=1, valTwo=2;

     int choice;

     BOOL fQuit = FALSE;



18:                          void (*pFunc)(int&, int&);

19:

     while (fQuit == FALSE)

     {

22:
cout << "(0)Quit (1)Change Values (2)Square (3)Cube
(4)Swap: ";
23:
cin >> choice;
24:
switch (choice)
25:
{
26:
case 1:pFunc = GetVals; break;
27:
case 2:pFunc = Square; break;
28:
case 3:pFunc = Cube; break;
29:
case 4:pFunc = Swap; break;
30:
default:fQuit = TRUE; break;
31:
}
32:
if (fQuit == TRUE)
33:
break;
34:
PrintVals ( pFunc, valOne, valTwo);
35:
}
36:

     return 0;

     }

39:

     void PrintVals( void (*pFunc)(int&, int&),int& x, int& y)

     {

     cout << "x: " << x << " y: " << y << endl;

     pFunc(x,y);

     cout << "x: " << x << " y: " << y << endl;

     }

Output: (0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 1 x: 1 y: 2

New value for ValOne: 2

New value for ValTwo: 3 x: 2 y: 3

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 3 x: 2 y: 3

x: 8 y: 27

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 2 x: 8 y: 27

x: 64 y: 729

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 4 x: 64 y: 729

x: 729 y:64

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 0

Analysis: On line 18, pFunc is declared to be a pointer to a function returning void and taking two parameters, both integer references. On line 9, PrintVals is declared to be a function taking three

parameters. The first is a pointer to a function that returns void but takes two integer reference parameters, and the second and third arguments to PrintVals are integer references. The user is again prompted for which functions to call, and then on line 34 PrintVals is called.

Go find a C++ programmer and ask him what this declaration means:

void PrintVals(void (*)(int&, int&),int&, int&);


This is the kind of declaration that you use infrequently and probably look up in the book each time you need it, but it will save your program on those rare occasions when it is exactly the required construct.

Using typedef with Pointers to Functions


The construct void (*)(int&, int&) is cumbersome, at best. You can use typedef to simplify this, by declaring a type VPF as a pointer to a function returning void and taking two integer references. Listing 14.9 rewrites Listing 14.8 using this typedef statement.


NOTE: To compile this program, place lines 46-80 of Listing 14.5 immediately after line 45.

Listing 14.9. Using typedef to make pointers to functions more readable.

1: // Listing 14.9. Using typedef to make pointers to functions more _readable

2:

3: #include <iostream.h> 4:

     void Square (int&,int&);

     void Cube (int&, int&);

     void Swap (int&, int &);

     void GetVals(int&, int&);

     typedef  void (*VPF) (int&, int&) ;

     void PrintVals(VPF,int&, int&);

     enum BOOL { FALSE, TRUE };

12:

     int main()

     {

     int valOne=1, valTwo=2;

     int choice;

     BOOL fQuit = FALSE;



19:           VPF pFunc;

20:

     while (fQuit == FALSE)

     {

     cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap:

";

     cin >> choice;

     switch (choice)

     {

     case 1:pFunc = GetVals; break;

     case 2:pFunc = Square; break;

     case 3:pFunc = Cube; break;

     case 4:pFunc = Swap; break;

     default:fQuit = TRUE; break;

     }

     if (fQuit == TRUE)

     break;

     PrintVals ( pFunc, valOne, valTwo);

     }

     return 0;

     }

39:

     void PrintVals( VPF pFunc,int& x, int& y)

     {

     cout << "x: " << x << " y: " << y << endl;

     pFunc(x,y);

     cout << "x: " << x << " y: " << y << endl;

     }

Output: (0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 1 x: 1 y: 2

New value for ValOne: 2

New value for ValTwo: 3 x: 2 y: 3

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 3 x: 2 y: 3

x: 8 y: 27

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 2 x: 8 y: 27

x: 64 y: 729

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 4 x: 64 y: 729

x: 729 y: 64

(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 0

Analysis: On line 9, typedef is used to declare VPF to be of the type "function that returns void and takes two parameters, both integer references."

On line 10, the function PrintVals() is declared to take three parameters: a VPF and two integer references. On line 19, pFunc is now declared to be of type VPF.

Once the type VPF is defined, all subsequent uses to declare pFunc and PrintVals() are much cleaner. As you can see, the output is identical.

Pointers to Member Functions


Up until this point, all of the function pointers you've created have been for general, non-class functions. It is also possible to create pointers to functions that are members of classes.


To create a pointer to member function, use the same syntax as with a pointer to function, but include the class name and the scoping operator (::). Thus, if pFunc points to a member function of the

class Shape, which takes two integers and returns void, the declaration for pFunc is the following:

void (Shape::*pFunc) (int, int);


Pointers to member functions are used in exactly the same way as pointers to functions, except that they require an object of the correct class on which to invoke them. Listing 14.10 illustrates the use of pointers to member functions.


Listing 14.10. Pointers to member functions.

1: //Listing 14.10 Pointers to member functions using virtual methods

2:

3: #include <iostream.h> 4:

     enum BOOL {FALSE, TRUE};

     class Mammal

     {

     public:

     Mammal():itsAge(1) {  }

     ~Mammal() { }

     virtual void Speak() const = 0;

     virtual void Move() const = 0;

     protected:

     int itsAge;

     };

16:

     class Dog : public Mammal

     {

     public:

     void Speak()const { cout << "Woof!\n"; }

     void Move() const { cout << "Walking to heel...\n"; }

     };

23:

24:

     class Cat : public Mammal

     {

     public:

     void Speak()const { cout << "Meow!\n"; }

     void Move() const { cout << "slinking...\n"; }

     };

31:

32:

     class Horse : public Mammal

     {

     public:

     void Speak()const { cout << "Winnie!\n"; }

     void Move() const { cout << "Galloping...\n"; }

     };

39:

40:

     int main()

     {

     void (Mammal::*pFunc)() const =0;

     Mammal* ptr =0;

     int Animal;

     int Method;

     BOOL fQuit = FALSE;



     while (fQuit == FALSE)

     {

51:
cout << "(0)Quit (1)dog (2)cat (3)horse: ";
52:
cin >> Animal;
53:
switch (Animal)
54:
{
55:
case 1: ptr = new Dog; break;
56:
case 2: ptr = new Cat; break;
57:
case 3: ptr = new Horse; break;
58:
default: fQuit = TRUE; break;
59:
}
60:
if (fQuit)
61:
break;
62:

63:
cout << "(1)Speak  (2)Move: ";
64:
cin >> Method;
65:
switch (Method)
66:
{
67:
case 1: pFunc = Mammal::Speak; break;
68:
default: pFunc = Mammal::Move; break;
69:
}
70:

71:
(ptr->*pFunc)();
72:
delete ptr;
     }

     return 0;

     }

Output: (0)Quit (1)dog (2)cat (3)horse: 1 (1)Speak (2)Move: 1

Woof!

(0)Quit (1)dog (2)cat (3)horse: 2

(1)Speak (2)Move: 1

Meow!

(0)Quit (1)dog (2)cat (3)horse: 3 (1)Speak (2)Move: 2

Galloping

(0)Quit (1)dog (2)cat (3)horse: 0

Analysis: On lines 6-15, the abstract data type Mammal is declared with two pure virtual methods, Speak() and Move(). Mammal is subclassed into Dog, Cat, and Horse, each of which overrides

Speak() and Move().

The driver program in main() asks the user to choose which type of animal to create, and then a new subclass of Animal is created on the free store and assigned to ptr on lines 55-57.

The user is then prompted for which method to invoke, and that method is assigned to the pointer pFunc. On line 71, the method chosen is invoked by the object created, by using the pointer ptr to

access the object and pFunc to access the function.

Finally, on line 72, delete is called on the pointer ptr to return the memory set aside for the object to the free store. Note that there is no reason to call delete on pFunc because this is a pointer to
code, not to an object on the free store. In fact, attempting to do so will generate a compile-time error.

Arrays of Pointers to Member Functions


As with pointers to functions, pointers to member functions can be stored in an array. The array can be initialized with the addresses of various member functions, and these can be invoked by offsets into the array. Listing 14.11 illustrates this technique.

Listing 14.11. Array of pointers to member functions.


1:                      //Listing 14.11 Array of pointers to member functions

2:

3:                      #include <iostream.h>

4:

5:                  enum BOOL {FALSE, TRUE};

6:

     class Dog

     {

     public:

     void Speak()const { cout << "Woof!\n"; }

     void Move() const { cout << "Walking to heel...\n"; }

     void Eat() const { cout << "Gobbling food...\n"; }

     void Growl() const { cout << "Grrrrr\n"; }

     void Whimper() const { cout << "Whining noises...\n"; }

     void RollOver() const { cout << "Rolling over...\n"; }

     void PlayDead() const { cout << "Is this the end of Little Caeser?\n"; }
     };

18:

     typedef void (Dog::*PDF)()const ;

     int main()

     {

     const int MaxFuncs = 7;

     PDF DogFunctions[MaxFuncs] =

24:                                     {       Dog::Speak,

25:
Dog::Move,
26:
Dog::Eat,
27:
Dog::Growl,
28:
Dog::Whimper,
29:
Dog::RollOver,
30:
Dog::PlayDead };
31:

     Dog* pDog =0;

     int Method;

     BOOL fQuit = FALSE;



     while (!fQuit)

     {

38:
cout <<
"(0)Quit (1)Speak (2)Move (3)Eat (4)Growl";
39:
cout << " (5)Whimper (6)Roll Over (7)Play Dead: ";
40:
cin >> Method;
41:
if (Method == 0)
42:
{

43:
fQuit = TRUE;
44:
break;

45:
}

46:
else

47:
{

48:
pDog = new Dog;
49:
(pDog->*DogFunctions[Method-1])();
50:
delete pDog;
51:
}

     }

     return 0;

     }

Output: (0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper

(6)Roll Over (7)Play Dead: 1

Woof!

(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over

(7)Play Dead: 4

Grrr

(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 7

Is this the end of Little Caeser?

(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 0

Analysis: On lines 7-17, the class Dog is created, with 7 member functions all sharing the same return type and signature. On line 19, a typedef declares PDF to be a pointer to a member function of Dog that takes no parameters and returns no values, and that is const: the signature of the 7 member

functions of Dog.

On lines 23-30, the array DogFunctions is declared to hold 7 such member functions, and it is initialized with the addresses of these functions.

On lines 38 and 39, the user is prompted to pick a method. Unless they pick Quit, a new Dog is created on the heap, and then the correct method is invoked on the array on line 49. Here's another good line to show to the hotshot C++ programmers in your company; ask them what this does:

(pDog->*DogFunctions[Method-1])();


Once again, this is a bit esoteric, but when you need a table built from member functions, it can make your program far easier to read and understand.


DO invoke pointers to member functions on a specific object of a class. DO use typedef to make pointer to member function declarations easier to read. DON'T use
pointer to member functions when there are simpler solutions.


Summary


Today you learned how to create static member variables in your class. Each class, rather than each object, has one instance of the static member variable. It is possible to access this member variable without an object of the class type by fully qualifying the name, assuming you've declared the static member to have public access.

Static member variables can be used as counters across instances of the class. Because they are not part of the object, the declaration of static member variables does not allocate memory, and static member variables must be defined and initialized outside the declaration of the class.

Static member functions are part of the class in the same way that static member variables are. They can be accessed without a particular object of the class, and can be used to access static member data. Static member functions cannot be used to access non-static member data because they do not have a this pointer.

Because static member functions do not have a this pointer, they also cannot be made constconst in a member function indicates that the this pointer is const.

You also learned how to declare and use pointers to functions and pointers to member functions. You saw how to create arrays of these pointers and how to pass them to functions.


Pointers to functions and pointers to member functions can be used to create tables of functions that can be selected from at runtime. This can give your program flexibility that is not easily achieved

without these pointers.

Q&A


Q. Why use static data when you can use global data?

A. Static data is scoped to the class. In this manner, static data are available only through an object of the class, through an explicit call using the class name if they are public, or by using a static member function. Static data are typed to the class type, however, and the restricted access and strong typing makes static data safer than global data.

            Why use static member functions when you can use global functions?

            Static member functions are scoped to the class, and can be called only by using an object of the class or an explicit full specification (such as ClassName::FunctionName()).

            Is it common to use many pointers to functions and pointers to member functions?

            No, these have their special uses, but are not common constructs. Many complex and powerful programs have neither.

Workshop


The Workshop contains quiz questions to help solidify your understanding of the material covered and exercises to provide you with experience in using what you've learned. Try to answer the quiz and exercise questions before checking the answers in Appendix D, and make sure you understand the answers before going to the next chapter.

Quiz


          Can static member variables be private?

          Show the declaration for a static member variable.

          Show the declaration for a static function pointer.

          Show the declaration for a pointer to function returning long and taking an integer parameter.

          Modify the pointer in Question 4 so it's a pointer to member function of class Car.

          Show the declaration for an array of 10 pointers as defined in Question 5.

Exercises



1. Write a short program declaring a class with one member variable and one static member

variable. Have the constructor initialize the member variable and increment the static member variable. Have the destructor decrement the member variable.

          Using the program from Exercise 1, write a short driver program that makes three objects and then displays their member variables and the static member variable. Then

destroy each object and show the effect on the static member variable.

          Modify the program from Exercise 2 to use a static member function to access the static member variable. Make the static member variable private.

          Write a pointer to member function to access the non-static member data in the program in Exercise 3, and use that pointer to print the value of that data.

          Add two more member variables to the class from the previous questions. Add accessor functions that get the value of these values, and give all the member functions the same return values and signatures. Use the pointer to member function to access these functions.


In Review


The Week in Review program for Week 2 brings together many of the skills you've acquired over the past fortnight and produces a powerful program.


This demonstration of linked lists utilizes virtual functions, pure virtual functions, function overriding, polymorphism, public inheritance, function overloading, forever loops, pointers, references, and more.

The goal of this program is to create a linked list. The nodes on the list are designed to hold parts, as might be used in a factory. While this is not the final form of this program, it does make a good demonstration of a fairly advanced data structure. The code list is 311 lines. Try to analyze the code on your own before reading the analysis that follows the output.

Listing R2.1. Week 2 in Review listing.


     // **************************************************

     //

2:                  // Title:                           Week 2 in Review

     //

4:                  // File:                           Week2

     //

     // Description:   Provide a linked list demonstration

program

     //

8:
// Classes:
PART - holds part numbers and potentially
other


9:
//
information about parts
10:
//

11:
//
PartNode - acts as a node in a PartsList
12:
//

13:
//
PartsList - provides the mechanisms for a
linked list Âof parts

     //

15:                  // Author:                       Jesse Liberty (jl)

     //

     // Developed:   486/66 32mb RAM  MVC 1.5

     //

19:                  // Target:                       Platform independent

     //

     // Rev History:  9/94 - First release (jl)

     //

     // **************************************************



25:                  #include <iostream.h>

26:

     typedef unsigned long ULONG;

     typedef unsigned short USHORT;





31:                  // **************** Part ************

32:

     // Abstract base class of parts

     class Part

     {

     public:

     Part():itsPartNumber(1) {}

     Part(ULONG PartNumber):itsPartNumber(PartNumber){}

     virtual ~Part(){};

     ULONG GetPartNumber() const { return itsPartNumber; }

     virtual void Display() const =0;  // must be overridden

     private:

     ULONG itsPartNumber;

     };

45:

     // implementation of pure virtual function so that

     // derived classes can chain up

     void Part::Display() const

     {

50:                                 cout << "\nPart Number: " << itsPartNumber << endl;

51:                  }

52:

53:                  // **************** Car Part ************

54:

     class CarPart : public Part

     {

     public:

     CarPart():itsModelYear(94){}

     CarPart(USHORT year, ULONG partNumber);

     virtual void Display() const

     {

62:                                     Part::Display(); cout << "Model Year: ";

63:                                     cout << itsModelYear << endl;

     }

     private:

     USHORT itsModelYear;

67:                  };

68:

     CarPart::CarPart(USHORT year, ULONG partNumber):

     itsModelYear(year),

     Part(partNumber)

     {}

73:

74:

75:                  // **************** AirPlane Part ************

76:

     class AirPlanePart : public Part

     {

     public:

     AirPlanePart():itsEngineNumber(1){};

     AirPlanePart(USHORT EngineNumber, ULONG PartNumber);

     virtual void Display() const

     {

84: Part::Display(); cout << "Engine No.: "; 85: cout << itsEngineNumber << endl;

     }

     private:

     USHORT itsEngineNumber;

     };

90:

     AirPlanePart::AirPlanePart(USHORT EngineNumber, ULONG

PartNumber):

     itsEngineNumber(EngineNumber),

     Part(PartNumber)

     {}

95:

     // **************** Part Node ************

     class PartNode

     {

     public:

     PartNode (Part*);

     ~PartNode();

     void SetNext(PartNode * node) { itsNext = node; }

     PartNode * GetNext() const;

     Part * GetPart() const;

     private:

     Part *itsPart;

     PartNode * itsNext;

     };


109:

110:                   // PartNode Implementations...

111:

     PartNode::PartNode(Part* pPart):

     itsPart(pPart),

     itsNext(0)

     {}

116:

     PartNode::~PartNode()

     {

     delete itsPart;

     itsPart = 0;

     delete itsNext;

     itsNext = 0;

     }

124:

     // Returns NULL if no next PartNode

     PartNode * PartNode::GetNext() const

     {

128:
return itsNext;
129:
}
130:

     Part * PartNode::GetPart() const

     {

     if (itsPart)

134:                                         return itsPart;

     else

136:                                         return NULL; //error

137:                   }

138:

     // **************** Part List ************

     class PartsList

     {

     public:

     PartsList();

     ~PartsList();

     // needs copy constructor and operator equals!

146:
Part*
Find(ULONG & position, ULONG PartNumber)
const;


147:
ULONG
GetCount() const { return itsCount; }
148:
Part*
GetFirst() const;
149:
static
PartsList& GetGlobalPartsList()
     {

151:                                 return        GlobalPartsList;

     }

153:
void
Insert(Part *);
154:
void
Iterate(void (Part::*f)()const) const;
155:
Part*
operator[](ULONG) const;
     private:

     PartNode * pHead;

     ULONG itsCount;

     static PartsList GlobalPartsList;

     };

161:

162:                   PartsList PartsList::GlobalPartsList;

163:

164:                   // Implementations for Lists...

165:

     PartsList::PartsList():

     pHead(0),

     itsCount(0)

     {}

170:

     PartsList::~PartsList()

     {

     delete pHead;

     }

175:

     Part*   PartsList::GetFirst() const

     {

     if (pHead)

179:                                         return pHead->GetPart();

     else

181:                                         return NULL;        // error catch here

182:                   }

183:

     Part *  PartsList::operator[](ULONG offSet) const

     {

     PartNode* pNode = pHead;

187:

     if (!pHead)

189:                                         return NULL; // error catch here

190:

     if (offSet > itsCount)

192:                                         return NULL; // error

193:

     for (ULONG i=0;i<offSet; i++)

195: pNode = pNode->GetNext(); 196:

     return   pNode->GetPart();

     }

199:

200:                   Part*           PartsList::Find(ULONG & position, ULONG

PartNumber)        const

     {

     PartNode * pNode = 0;

     for (pNode = pHead, position = 0;

204:
pNode!=NULL;
205:
pNode = pNode->GetNext(), position++)
     {

207:                                         if (pNode->GetPart()->GetPartNumber() == PartNumber)

208:                                                    break;

     }

     if (pNode == NULL)

211:                                         return NULL;

     else

213: return pNode->GetPart(); 214: } 215:

     void PartsList::Iterate(void (Part::*func)()const) const

     {

     if (!pHead)

219:                                         return;

     PartNode* pNode = pHead;

     do

222:                                         (pNode->GetPart()->*func)();

     while (pNode = pNode->GetNext());

     }

225:

     void PartsList::Insert(Part* pPart)

     {

     PartNode * pNode = new PartNode(pPart);

     PartNode * pCurrent = pHead;

     PartNode * pNext = 0;

231:

     ULONG New =  pPart->GetPartNumber();

     ULONG Next = 0;

     itsCount++;

235:

     if (!pHead)

     {

238:
pHead = pNode;
239:
return;
240:
}
241:

     // if this one is smaller than head

     // this one is the new head

     if (pHead->GetPart()->GetPartNumber() > New)

     {


246:
pNode->SetNext(pHead);
247:
pHead = pNode;

248:
return;
249:
}
250:

     for (;;)

     {

253:
// if there is no next, append this new one
254:
if (!pCurrent->GetNext())
255:
{
256:
pCurrent->SetNext(pNode);
257:
return;
258:
}
259:

260:
// if this goes after this one and before the next
261:
// then insert it here, otherwise get the next
262:
pNext = pCurrent->GetNext();
263:
Next = pNext->GetPart()->GetPartNumber();
264:
if (Next > New)
265:
{
266:
pCurrent->SetNext(pNode);
267:
pNode->SetNext(pNext);
268:
return;
269:
}
270:
pCurrent = pNext;
     }

     }



     int main()

     {

     PartsList pl = PartsList::GetGlobalPartsList();

     Part * pPart = 0;

     ULONG PartNumber;

     USHORT value;

     ULONG choice;

281:

     while (1)

     {

284:
cout << "(0)Quit (1)Car (2)Plane: ";
285:
cin >> choice;
286:

287:
if (!choice)
288:
break;
289:

290:
cout << "New PartNumber?: ";
291:
cin >>  PartNumber;
292:

293:
if (choice == 1)

294:
{
295:
cout << "Model Year?: ";
296:
cin >> value;
297:
pPart = new CarPart(value,PartNumber);
298:
}
299:
else
300:
{
301:
cout << "Engine Number?: ";
302:
cin >> value;
303:
pPart = new AirPlanePart(value,PartNumber);
304:
}
305:

306:
pl.Insert(pPart);
     }

     void (Part::*pFunc)()const = Part::Display;

     pl.Iterate(pFunc);

     return 0;

     }

Output: (0)Quit (1)Car (2)Plane: 1

New PartNumber?: 2837

Model Year? 90

(0)Quit (1)Car (2)Plane: 2

New PartNumber?: 378

Engine Number?: 4938

(0)Quit (1)Car (2)Plane: 1

New PartNumber?: 4499

Model Year? 94

(0)Quit (1)Car (2)Plane: 1 New PartNumber?: 3000

Model Year? 93

(0)Quit (1)Car (2)Plane: 0

Part Number: 378

Engine No.: 4938

Part Number: 2837

Model Year: 90

Part Number: 3000

Model Year: 93

Part Number: 4499

Model Year: 94

Analysis: The Week 2 in Review listing provides a linked list implementation for Part objects. A linked list is a dynamic data structure; that is, it is like an array but it is sized to fit as objects are

added and deleted.

This particular linked list is designed to hold objects of class Part, where Part is an abstract data
type serving as a base class to any objects with a part number. In this example, Part has been subclassed into CarPart and AirPlanePart.

Class Part is declared on lines 34-44, and consists of a part number and some accessors. Presumably this class could be fleshed out to hold other important information about the parts, such as what

components they are used in, how many are in stock, and so forth. Part is an abstract data type, enforced by the pure virtual function Display().

Note that Display() does have an implementation, on lines 48-51. It is the designer's intention that derived classes will be forced to create their own Display() method, but may chain up to this
method as well.

Two simple derived classes, CarPart and AirPlanePart, are provided on lines 55-67 and 77-89, respectively. Each provides an overridden Display() method, which does in fact chain up to the base class Display() method.

The class PartNode serves as the interface between the Part class and the PartList class. It

contains a pointer to a part and a pointer to the next node in the list. Its only methods are to get and set the next node in the list and to return the Part to which it points.

The intelligence of the list is, appropriately, in the class PartsList, whose declaration is on lines 140-160. PartsList keeps a pointer to the first element in the list (pHead) and uses that to access

all other methods by walking the list. Walking the list means asking each node in the list for the next node, until you reach a node whose next pointer is NULL.

This is only a partial implementation; a fully developed list would provide either greater access to its first and last nodes, or would provide an iterator object, which allows clients to easily walk the list.

PartsList nonetheless provides a number of interesting methods, which are listed in alphabetical order. This is often a good idea, as it makes finding the functions easier.

Find() takes a PartNumber and a ULONG. If the part corresponding to PartNumber is found, it returns a pointer to the Part and fills the ULONG with the position of that part in the list. If PartNumber is not found, it returns NULL, and the position is meaningless.

GetCount() returns the number of elements in the list. PartsList keeps this number as a member variable, itsCount, though it could, of course, compute this number by walking the list.

GetFirst() returns a pointer to the first Part in the list, or returns NULL if the list is empty.

GetGlobalPartsList() returns a reference to the static member variable GlobalPartsList. This is a static instance of this class; every program with a PartsList also has one GlobalPartsList, though, of course, it is free to make other PartsLists as well. A full

implementation of this idea would modify the constructor of Part to ensure that every part is created on the GlobalPartsList.

Insert takes a pointer to a Part, creates a PartNode for it, and adds the Part to the list, ordered by PartNumber.

Iterate takes a pointer to a member function of Part, which takes no parameters, returns void, and is const. It calls that function for every Part object in the list. In the example program this is called on Display(), which is a virtual function, so the appropriate Display() method will be

called based on the runtime type of the Part object called.

Operator[] allows direct access to the Part at the offset provided. Rudimentary bounds checking is provided; if the list is NULL or if the offset requested is greater than the size of the list, NULL is
returned as an error condition.


Note that in a real program these comments on the functions would be written into the class declaration.

The driver program is on lines 274-311. A pointer to PartsList is declared on line 266 and initialized with GlobalPartsList. Note that GlobalPartsList is initialized on line 162. This

is necessary as the declaration of a static member variable does not define it; definition must be done outside the declaration of the class.


On lines 282-307, the user is repeatedly prompted to choose whether to enter a car part or an airplane part. Depending on the choice the right value is requested, and the appropriate part is created. Once created, the part is inserted into the list on line 306.

The implementation for the Insert() method of PartsList is on lines 226-272. When the first part number is entered, 2837, a CarPart with that part number and the model year 90 is created and passed in to LinkedList::Insert().

On line 228, a new PartNode is created with that part, and the variable New is initialized with the part number. The PartsList's itsCount member variable is incremented on line 234.

On line 236, the test that pHead is NULL will evaluate TRUE. Since this is the first node, it is true that the PartsList's pHead pointer has zero. Thus, on line 238, pHead is set to point to the new
node and this function returns.

The user is prompted to enter a second part, and this time an AirPlane part with part number 378 and engine number 4938 is entered. Once again PartsList::Insert() is called, and once again pNode is initialized with the new node. The static member variable itsCount is incremented to 2, and pHead is tested. Since pHead was assigned last time, it is no longer null and the test fails.

On line 244, the part number held by pHead2837, is compared against the current part number, 378. Since the new one is smaller than the one held by pHead, the new one must become the new

head pointer, and the test on line 244 is true.

On line 246, the new node is set to point to the node currently pointed to by pHead. Note that this does not point the new node to pHead, but rather to the node that pHead was pointing to! On line 247, pHead is set to point to the new node.

The third time through the loop, the user enters the part number 4499 for a Car with model year 94.

The counter is incremented and the number this time is not less than the number pointed to by pHead, so the for loop that begins on line 251 is entered.

The value pointed to by pHead is 378. The value pointed to by the second node is 2837. The current value is 4499. The pointer pCurrent points to the same node as pHead and so has a next value; pCurrent points to the second node, and so the test on line 254 fails.

The pointer pCurrent is set to point to the next node and the loop repeats. This time the test on line 254 succeeds. There is no next item, so the current node is told to point to the new node on line 256, and the insert is finished.

The fourth time through, the part number 3000 is entered. This proceeds just like the previous iteration, but this time when the current node is pointing to 2837 and the next node has 4499, the test on line 264 returns TRUE and the new node is inserted into position.

When the user finally presses 0, the test on line 287 evaluates true and the while(1) loop breaks. On line 308, the member function Display() is assigned to the pointer to member function pFunc.
In a real program this would be assigned dynamically, based on the user's choice of method.

The pointer to member function is passed to the PartsList Iterate() method. On line 216, the Iterate() method ensures that the list is not empty. Then, on lines 221-223, each Part on the list is called using the pointer to member function. This calls the appropriate Display() method for the Part, as shown in the output.

0 comments:

Post a Comment