Structures: C-Style

 < Day Day Up > 



Structures are a double-edged sword in C++. Some authors do not bother to give structures much coverage because they are closely related to classes in capability, as you will soon see. I take the opposite approach because I can foresee times when a C++ programmer will be tasked with either maintaining C code or converting legacy C code to C++. When these situations arise it is helpful to know the procedural mind set behind the use of structs.

In C, a structure is a set of heterogeneous data elements grouped together to form a new data type. The C struct mind set separates the structure from the functions that manipulate structure elements. It goes something like this: declare a structure and put in it any required data elements, then, declare and write some functions that can be used to manipulate the structure’s data elements. The C way of thinking of structures is decidedly procedural.

Let us examine an extended C-style example that declares a struct named Person and some functions that manipulate Person data elements. Example 10.8 gives the source code for the personstruct.h header file.

Listing 10.8: personstruct.h

start example
 1  #ifndef __PERSON_STRUCT_H  2  #define __PERSON_STRUCT_H  3  4  namespace HairColor{  5      enum HairColor {Black, Red, Auburn, Brown, Blond, Silver, Grey};  6  }  7  8  namespace EyeColor{  9      enum EyeColor {Black, Hazel, Blue, Brown}; 10  } 11 12  const int NAMESIZE = 26; 13 14  struct Person { 15     char f_name[NAMESIZE]; 16     char l_name[NAMESIZE]; 17     HairColor::HairColor hair_color; 18     EyeColor::EyeColor eye_color; 19  }; 20  #endif
end example

In example 10.8 two name spaces are declared that contain enum type declarations. You have seen these in previous examples above. On line 12 a constant named NAMESIZE is declared and initialized to the value 26. NAMESIZE is used to set the size of each character array declared in the body of the Person structure.

The declaration of the Person structure begins on line 14. The Person structure contains four elements: two character arrays named f_name and l_name to contain a person’s first name and last name respectively, a hair_color attribute of type HairColor::HairColor and an eye_color attribute of type EyeColor::EyeColor. The data elements of Person represent the attributes of a particular person.

Before moving on I would like to show you how to access structural elements.

Accessing Structural Elements

There are two ways to access the data elements appearing inside of a structure. If you have an ordinary variable of a struct data type you can use the dot “.” operator. If you have a pointer to a struct object then you can use either the dot operator applied to a dereferenced pointer or the shorthand member access “->” operator. Let us examine each of these structural member access methods a little closer.

Accessing Structural Data Members Via the Dot “.” Operator

Given a variable of type Person...:

Person Bill;

...you can access Bill’s f_name element using the dot operator like so:

Bill.f_name

Having access to each of Bill’s elements in this manner offers you the ability to manipulate Bill in just about any way you need to as shown in the following code:

Listing 10.9: accessing struc telements

start example
   1  strcpy(Bill.f_name, "Bill");  2  strcpy(Bill.l_name, "Smith");  3  Bill.eye_color = EyeColor::Blue;  4  Bill.hair_color = HairColor::Blond;   5  6  cout<<"First Name: "<<Bill.f_name<<endl;  7  cout<<" Last Name: "<<Bill.l_name<<endl;  8  cout<<"Hair Color: "<<Bill.hair_color<<endl;  9  cout<< "Eye Color: "<<Bill.eye_color<<endl;
end example

This code, when appearing inside a main() function, should produce the results shown in figure 10-1:

click to expand
Figure 10-1: Example 10.9 Output

Wait a second! Notice the output shown for hair color and eye color in figure 10-1. This highlights a slight problem with enumerated data types; they are simply integer values. If your compiler gives you a problem compiling the code shown in example 10.9 try adjusting the C++ language settings so enums are treated as ordinary integers. You may have to do this if you have problems sending enum variables or elements to the output stream. The C++ language settings window for CodeWarrior is shown in figure 10-2.

click to expand
Figure 10-2: C++ Language Settings: Set Enums Always int

Accessing Structural Elements Via the Shorthand Member Access “->” Operator

You can use the “->” operator to access structural elements via pointers. Examine the following code:

Listing 10.10: accessing struct elements via pointers

start example
 1  Person* Bill = new Person;  2  3  strcpy(Bill->f_name, "Bill");  4  strcpy(Bill->l_name, "Smith");  5  Bill->eye_color = EyeColor::Blue;  6  Bill->hair_color = HairColor::Blond;  7  8  cout<<"First Name: "<<Bill->f_name<<endl;  9  cout<<" Last Name: "<<Bill->l_name<<endl; 10  cout<<"Hair Color: "<<Bill->hair_color<<endl; 11  cout<<" Eye Color: "<<Bill->eye_color<<endl; 12 13  delete Bill;
end example

On line 1 an object of type Person is dynamically allocated from the heap. Using the pointer Bill, you can access Bill’s data members using the short-hand member access operator “->” as is shown throughout the rest of the program. An alternative to the short-hand access operator is the long way, which is simply dereferencing the pointer and applying the dot operator like so:

strcpy((*Bill).f_name, “Bill”); //The long way

Continuing with the C-style way of structure programming, the following header file declares several functions that will be used to make manipulating Person objects slightly easier. Example 10.11 shows a header file named personfunctions.h that contains eight functions specifically designed to manipulate Person structural elements. Example 10.12 shows the implementation file for these functions named personfunctions.cpp.

Listing 10.11: personfunctions.h

start example
 1  #ifndef __PERSON_FUNCTIONS_H  2  #define __PERSON_FUNCTIONS_H  3  #include "personstruct.h"  4  5  void setFirstName(Person& person, char* f_name);  6  void setLastName(Person& person, char* l_name);  7  void setHairColor(Person& person, HairColor::HairColor hair_color);  8  void setEyeColor(Person& person, EyeColor::EyeColor eye_color);  9  char* getFirstName(Person& person); 10  char* getLastName(Person& person); 11  char* getHairColor(Person& person); 12  char* getEyeColor(Person& person); 13 14  #endif
end example

Listing 10.12: personfunctions.cpp

start example
 1  #include "personstruct.h"  2  #include "personfunctions.h"  3  #include "string.h"  4  5  char HairColorStrings[][7] = {{"Black"}, {"Red"}, {"Auburn"}, {"Brown"}, {"Blond"},  6  {"Silver"}, {"Grey"}};  7  char EyeColorStrings [][7] = {{"Black"}, {"Hazel"}, {"Blue"}, {"Brown"}};  8  9  void setFirstName(Person& person, char* f_name){ 10      strcpy(person.f_name, f_name); 11  } 12 13  void setLastName(Person& person, char* l_name){ 14      strcpy(person.l_name, l_name); 15  } 16 17  void setHairColor(Person& person, HairColor::HairColor hair_color){ 18      person.hair_color = hair_color; 19  } 20 21  void setEyeColor(Person& person, EyeColor::EyeColor eye_color){ 22      person.eye_color = eye_color; 23  } 24 25  char* getFirstName(Person& person){ 26      return person.f_name; 27  }  28  29  char* getLastName(Person& person){ 30      return person.l_name; 31  } 32  char* getHairColor(Person& person){ 33      char* hair_color = NULL; 34      switch(person.hair_color){ 35           case HairColor::Black : hair_color = HairColorStrings[HairColor::Black]; 36                                    break; 37           case HairColor::Red : hair_color = HairColorStrings[HairColor::Red]; 38                                    break; 39           case HairColor::Auburn : hair_color = HairColorStrings[HairColor::Auburn]; 40                                    break; 41           case HairColor::Brown : hair_color = HairColorStrings[HairColor::Brown]; 42                                    break; 43           case HairColor::Blond : hair_color = HairColorStrings[HairColor::Blond]; 44                                    break; 45           case HairColor::Silver : hair_color = HairColorStrings[HairColor::Silver]; 46                                    break; 47           case HairColor::Grey : hair_color = HairColorStrings[HairColor::Grey]; 48                                    break; 49           default : hair_color = HairColorStrings[HairColor::Blond]; 50      } 51    return hair_color; 52  } 53  char* getEyeColor(Person& person){   54      char* eye_color = NULL;   55      switch(person.eye_color){   56           case EyeColor::Black : eye_color = EyeColorStrings[EyeColor::Black];   57                                    break; 58           case EyeColor::Hazel : eye_color = EyeColorStrings[EyeColor::Hazel];   59                                    break; 60           case EyeColor::Blue : eye_color = EyeColorStrings[EyeColor::Blue];   61                                    break; 62           case EyeColor::Brown : eye_color = EyeColorStrings[EyeColor::Brown];   63                                    break; 64           default : eye_color = EyeColorStrings[EyeColor::Blue];   67      } 66    return eye_color;   67  }
end example

Referring to example 10.12, on lines 5 and 7 two arrays of strings are declared and initialized to hold the string representations of both EyeColor and HairColor. These arrays are used, along with their related enumerated types, in the last two functions to render a Person object’s hair_color and eye_color attribute into a string representation.

The functions in example 10.12 can be classified two ways: functions that change or set a person’s attributes, and functions that simply return the value of a person’s attributes. Functions that change or manipulate structural data members are referred to as mutator functions; functions that simply return the state of a data member are referred to as accessor functions.

The setFirstName() function takes the first argument, a reference to a Person object named person, and sets person’s f_name element to the string of characters pointed to by the second argument. Notice how setFirstName() is simply calling the strcpy() library function. The strcpy() function is a standard C++ library functions whose declaration can be found in the string.h header file. The rest of the set functions operate in similar fashion, although setHairColor() and setEyeColor() need only make a simple assignment to complete their mission.

The getFirstName() and getLastName() functions return pointers to the person object’s f_name and l_name arrays. The two functions getHairColor() and getEyeColor() are somewhat more involved. Both getHairColor() and getEyeColor() operate the same so only the getHairColor() function will be discussed in detail.

The getHairColor() function takes a reference to a person object and in the switch evaluation section checks the state of the hair_color attribute and compares it with each of the HairColor::HairColor states. When the matching case is found, the local char pointer variable hair_color is set to point to the proper string located in the HairColorStrings array. Assume for a moment that the person’s hair_color was Black. The first case statement would apply and the local hair_color variable would be set to point to the string “Black” located in the HairColorStrings array by using the enumeration state HairColor::Black as an offset value into the array. The main() function in example 10.13 shows these functions in action on several person objects.

Listing 10.13: main.cpp

start example
 1  #include <iostream>  2  #include "personstruct.h"  3  #include "personfunctions.h"  4  using namespace std;   5  6  int main(){  7       Person Bob;  8       setFirstName(Bob, "Bob");  9       setLastName(Bob, "Smith"); 10       setHairColor(Bob, HairColor::Red); 11       setEyeColor(Bob, EyeColor::Blue); 12       cout<<"First Name: "<<getFirstName(Bob)<<endl; 13       cout<<" Last Name: "<<getLastName(Bob)<<endl; 14       cout<<"Hair Color: "<<getHairColor(Bob)<<endl; 15       cout<<" Eye Color: "<<getEyeColor(Bob)<<endl; 16       cout<<endl<<endl; 17 18       Person* Bill = new Person; 19       setFirstName(*Bill, "Bill"); 20       setLastName(*Bill, "Jones"); 21       setHairColor(*Bill, HairColor::Blond);   22       setEyeColor(*Bill, EyeColor::Brown);   23       cout<<"First Name: "<<getFirstName(*Bill)<<endl;   24       cout<<" Last Name: "<<getLastName(*Bill)<<endl;   25       cout<<"Hair Color: "<<getHairColor(*Bill)<<endl;   26       cout<<" Eye Color: "<<getEyeColor(*Bill)<<endl;   27       delete Bill; 28 29       return 0; 30  }
end example

Referring to example 10.13, on line 7 a Person variable named Bob is declared. On lines 8 through 11 each of the set functions is called using Bob as the first argument to set each of Bob’s attributes. Bob’s attributes are then printed to the screen on lines 12 through 15 using the get functions.

On line 18 another Person object is created dynamically and its address is assigned to the Person pointer Bill. Since Bill is a pointer the “*” operator must be used on Bill to gain access to what Bill points to, namely, a Person object in dynamic memory. Otherwise, all the functions perform the same tasks on Bill as they do on Bob.

Quick Summary

C-style structures provide a way to collect related data elements together and create a new composite data type. The Person structure can be thought of as stamp or a mold with which new Person objects can be created. Every Person object created will have four attributes: f_name, l_name, hair_color, and eye_color.

There are two primary ways to access structure data elements. When dealing with objects, use the dot “.” operator. When dealing with pointers to objects use the shorthand member access “->” operator and let the compiler do the pointer dereferencing for you.

The C-style way of using structures is based on procedural programming techniques. In the procedural programming paradigm, data structures are defined separately from the functions used to manipulate or access those data structures.



 < Day Day Up > 



C++ for Artists. The Art, Philosophy, and Science of Object-Oriented Programming
C++ For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504028
EAN: 2147483647
Year: 2003
Pages: 340
Authors: Rick Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net