Structures: C-Style

 < Day Day Up > 



Structures: C++-Style

In the previous section I introduced you to structures and showed you an example of using structures with a C- style, procedural mind set. In this section I want to show you what structures can do in C++. This section marks the beginning of your initiation into the world of object-oriented programming.

In C++, structures are more than just a collection of heterogeneous data elements. Structures can contain both data elements and the functions that manipulate those data elements. Let us revisit the Person structure and redesign it in C++ fashion, combining both the data elements and the functions needed to manipulate those data elements into one structure.

Person Structure Redesign

Interestingly enough, it will be much easier to completely redesign the functions rather than copy and paste them from the previous project so that’s what I will do. Example 10.14 gives the revised header file personstruct.h.

Listing 10.14: 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       public: 16           void setFirstName(char* _f_name); 17           void setLastName(char* _l_name); 18           void setHairColor(HairColor::HairColor _hair_color); 19           void setEyeColor(EyeColor::EyeColor _eye_color); 20           char* getFirstName(); 21           char* getLastName(); 22           char* getHairColor(); 23           char* getEyeColor(); 24 25       private:   26           char f_name[NAMESIZE];   27           char l_name[NAMESIZE];   27           HairColor::HairColor hair_color;   28           EyeColor::EyeColor eye_color; 30  }; 31  #endif
end example

OK, what is going on here? This version of the Person struct looks a lot different from its C-style counterpart. Namely, both structure data elements like the f_name and l_name arrays, hair_color and eye_color, and functions, are all part of the structure. This forces the functions to be slightly redesigned. No longer are the functions separate from the data structure they are intended to manipulate, so, there is no need to supply a reference to a Person object.

Structure functions have complete access to all structure attributes. Because the functions have access to Person data elements the function parameter names that remain were changed to make them unique. For example, in the setFirstName() function, an underscore “_” was added to the parameter f_name to make it _f_name. Making member function parameter names unique eliminates potential name conflicts between structural data members and member function parameter names.

Public Interface Functions and the Public Access Specifier

With the addition of the functions to Person struct comes the notion of an interface to Person objects. The functions represent operations that can be performed on Person objects. The functions have been collectively declared as being publicly accessible through the use of the public access specifier appearing on line 15 in example 10.14. C++ structures have public accessibility by default so the use of the public access specifier at the top of the structure is redundant but a good documentation technique. The set of public interface functions is intended to be the only authorized way to set, manipulate, or otherwise access any object’s attributes.

Private Data Members and the Private Access Specifier

Since the public interface functions are going to be the only authorized form of access into the internals of a Person object, Person data elements have been declared as being private through the use of the private access specifier. A private data element is accessible only from within the structure itself. This means that all the functions have access to the private data elements but no data element can be accessed directly from outside an object. Observe the following example:

Person Bob; Bob.setFirstName("Bob"); //OK...Public function cout<<Bob.f_name<<endl; //Error! Private attribute

These few lines of code teach us a lot. First, a Person object named Bob is declared. Next, because the setFirstName() function is a part of the Person structure, it is called on an object of type Person rather than with an object of type Person as an argument, as was done in the C-style example. Since the setFirstName() function is declared as being public, its use on a Person object is authorized. However, since f_name is declared as being private, its use in this fashion is unauthorized and your compiler will produce an error stating that you have attempted to access a private data member. Now that you have seen the personstruct.h header file let us take look at the implementation file, shown in example 10.15.

Listing 10.15: personstruct.cpp

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

Referring to example 10.15, compare example 10.15 against example 10.12. The string arrays declared at the beginning of the file remain unchanged from the previous version. The format of each function has changed somewhat to reflect the fact that the functions are now declared inside the Person structure namespace. The format for defining a function declared inside a structure is shown in figure 10-3.

click to expand
Figure 10-3: Format of Structure Function Definition

Since the setFirstName() function belongs to the Person structure, it has direct access to Person’s f_name data element. All it needs to do is call the strcpy() function using Person’s f_name and its _f_name parameter. This is were things start to get somewhat confusing, so hang on. To what Person object does f_name belong? Well, it depends upon what object the function was called.

Observe the following code:

Person Bob, Bill; Bob.setFirstName(“Bob”); //called on the Bob object Bill.setFirstName(“Bill”); //called on the Bill object

Here, two Person objects are declared, Bob and Bill. Next, setFirstName() is called on the Bob object, and Bob’s f_name array is initialized to the string “Bob”. Then, the same function is called on the Bill object and the same operation is performed, only on Bill’s f_name array instead of Bob’s.

The Deep Secret: The this Pointer

Well, it is not really a secret but I got your attention didn’t I? When objects are created from structures there is a different set of data elements for each object but only one set of functions. The name of the object upon which a function is called directs the function’s actions to a specific set of data elements pointed to by the object name. You can differentiate between structure member data elements and function parameters having the same name by prefixing the keyword “this” to a data element as shown in the following code:

void Person::setFirstName(char* f_name){    strcpy(this.f_name, f_name); }

Notice in this example the underscore was removed from the parameter _f_name to yield f_name. However, to distinguish between the two identical identifiers the this keyword must be added to the one that belongs to the object. You will see many good uses for the this pointer before you reach the end of this book! A thorough treatment of the this pointer is offered in chapter 11, so for now, just be aware of its existence. Example 10.16 shows a main() function using the new Person struct with its built-in functions:

Listing 10.16: main.cpp testing Person struct

start example
 1  #include <iostream>  2  #include "personstruct.h"  3  using namespace std;    4  5  int main(){  6      Person Bob;  7  8      Bob.setFirstName("Bob");  9      Bob.setLastName("Smith"); 10      Bob.setHairColor(HairColor::Black); 11      Bob.setEyeColor(EyeColor::Blue); 12 13      cout<<"First Name: "<<Bob.getFirstName()<<endl 14          <<" Last Name: "<<Bob.getLastName()<<endl 15          <<"Hair Color: "<<Bob.getHairColor()<<endl 16          <<" Eye Color: "<<Bob.getEyeColor()<<endl; 17 18      cout<<endl<<endl; 19      return 0; 20  }
end example

Quick Summary

The C++-style of using structures is decidedly object-oriented. Both structural data elements and the functions that manipulate those data elements are combined into one structure to form a new data type. New data types created by you the programmer, be they structures done C-style or C++-style, enums, or classes, are collectively referred to as user-defined types. You will also see and hear the term abstract data type used to describe new data types created by programmers in an effort to abstract the problem being solved.

Structure data and function members have public accessibility by default. You can control which members have what level of accessibility by using access specifiers. Two different access specifiers were discussed above: public and private. Declaring the member functions of a structure public while keeping its data members private is called data encapsulation. As a rule, and it is a good rule, only the member functions of a structure need have direct access to a structure’s data members. The rest of this book is largely devoted to showing you why this is a good object-oriented design policy.



 < 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