Monday, November 2, 2009

3.6. Data Members, set Functions and get Functions



3.6. Data Members, set Functions and get
Functions


In Chapter
2, we declared all of a program's variables in its
main function. Variables declared in a
function definition's body are known as local
variables
and can be used only from the line of
their declaration in the function to the immediately following closing right
brace (}). A local variable must be declared
before it can be used in a function. A local variable cannot be accessed outside
the function in which it is declared. When a function terminates, the values of
its local variables are lost. (You'll see an exception to this in Chapter
6 when we discuss static local variables.) Recall from Section
3.2 that an object has attributes that are carried with
it as it is used in a program. Such attributes exist throughout the life of the
object.


A class normally consists of one or
more member functions that manipulate the attributes that belong to a particular
object of the class. Attributes are represented as variables in a class
definition. Such variables are called data
members
and are declared inside a class definition
but outside the bodies of the class's member-function definitions. Each object
of a class maintains its own copy of its attributes in memory. The example in
this section demonstrates a GradeBook class that
contains a courseName data member to represent a particular
GradeBook object's course name.


GradeBook Class with a Data Member, a set Function and a get
Function


In our next example, class GradeBook (Fig. 3.5)
maintains the course name as a data member so that it can be used or modified at
any time during a program's execution. The class contains member functions
setCourseName, getCourseName and
displayMessage. Member function setCourseName stores a course name in a GradeBook data member.
Member function getCourseName obtains the
course name from that data member. Member function displayMessage—which now specifies no parameters—still displays a
welcome message that includes the course name. However, as you'll see, the
function now obtains the course name by calling another function in the same
class—getCourseName.













Fig. 3.5. Defining and testing class
GradeBook with a data member and set and
get functions.


 

 1   // Fig. 3.5: fig03_05.cpp
2 // Define class GradeBook that contains a courseName data member
3 // and member functions to set and get its value;
4 // Create and manipulate a GradeBook object with these functions.
5 #include <iostream>
6 using std::cout;
7 using std::cin;
8 using std::endl;
9
10 #include <string> // program uses C++ standard string class
11 using std::string;
12 using std::getline;
13
14 // GradeBook class definition
15 class GradeBook
16 {
17 public:
18 // function that sets the course name
19 void setCourseName( string name )
20 {
21 courseName = name; // store the course name in the object
22 } // end function setCourseName
23
24 // function that gets the course name
25 string getCourseName()
26 {
27 return courseName; // return the object's courseName
28 } // end function getCourseName
29
30 // function that displays a welcome message
31 void displayMessage()
32 {
33 // this statement calls getCourseName to get the
34 // name of the course this GradeBook represents
35 cout << "Welcome to the grade book for\n" << getCourseName() << "!"
36 << endl;
37 } // end function displayMessage
38 private:
39 string courseName; // course name for this GradeBook
40 }; // end class GradeBook
41
42 // function main begins program execution
43 int main()
44 {
45 string nameOfCourse; // string of characters to store the course name
46 GradeBook myGradeBook; // create a GradeBook object named myGradeBook
47
48 // display initial value of courseName
49 cout << "Initial course name is: " << myGradeBook.getCourseName()
50 << endl;
51
52 // prompt for, input and set course name
53 cout << "\nPlease enter the course name:" << endl;
54 getline( cin, nameOfCourse ); // read a course name with blanks
55 myGradeBook.setCourseName( nameOfCourse ); // set the course name
56
57 cout << endl; // outputs a blank line
58 myGradeBook.displayMessage(); // display message with new course name
59 return 0; // indicate successful termination
60 } // end main



Initial course name is:

Please enter the course name:
CS101 Introduction to C++ Programming

Welcome to the grade book for
CS101 Introduction to C++ Programming!




Good Programming Practice 3.1








Place a blank line between
member-function definitions to enhance program
readability.



A typical
instructor teaches multiple courses, each with its own course name. Line 39
declares that courseName is a variable of type string. Because the variable is declared in the class
definition (lines 15–40) but outside the bodies of the class's member-function
definitions (lines 19–22, 25–28 and 31–37), the variable is a data member. Every
instance (i.e., object) of class GradeBook
contains one copy of each of the class's data members—if there are two
GradeBook objects, each has its own copy of courseName (one
per object), as you'll see in the example of Fig.
3.7. A benefit of making courseName a data
member is that all the member functions of the class (in this case, class
GradeBook) can manipulate any data members
that appear in the class definition (in this case, courseName).


Access Specifiers public and private


Most data-member declarations appear
after the access-specifier label private: (line 38). Like
public, keyword private is an
access specifier. Variables or functions declared after access specifier private (and before the next access specifier) are accessible
only to member functions of the class for which they are declared. Thus, data
member courseName can be used only in member
functions setCourseName, getCourseName and
displayMessage of (every object of) class GradeBook. Data
member courseName, because it is private, cannot be accessed
by functions outside the class (such as main)
or by member functions of other classes in the program. Attempting to access
data member courseName in one of these
program locations with an expression such as myGradeBook.courseName would result in a compilation error containing a
message similar to


cannot access private member declared in class 'GradeBook'



Software Engineering Observation 3.1








As a rule of thumb, data members should
be declared private and member functions should be declared
public. (We'll see that it is appropriate to declare certain member
functions private, if they are to be
accessed only by other member functions of the
class.)




Common Programming Error
3.6








An attempt by
a function, which is not a member of a particular class (or a friend of that
class, as we'll see in Chapter
10, Classes: A Deeper Look, Part 2), to access a
private member of that class is a compilation
error.



The default access for class members is
private so all members after the class
header and before the first access specifier are private. The access
specifiers public and private may be
repeated, but this is unnecessary and can be confusing.



Good Programming Practice 3.2








Despite the fact that
the public and private access specifiers may be repeated and
intermixed, list all the public members of a
class first in one group and then list all the private members in another group. This focuses the client's
attention on the class's public interface,
rather than on the class's
implementation.




Good Programming Practice 3.3








If you choose to
list the private members first in a class
definition, explicitly use the private access
specifier despite the fact that private is assumed by default. This
improves program clarity.



Declaring data members with access specifier private
is known as data hiding. When a program
creates (instantiates) a GradeBook object, data member
courseName is encapsulated (hidden) in the
object and can be accessed only by member functions of the object's class. In
class GradeBook, member functions setCourseName and
getCourseName manipulate the data member courseName directly
(and displayMessage could do so if necessary).



Software Engineering Observation 3.2








You'll see in Chapter
10 that functions and classes declared by a class to
be friends
can access the private
members of the class.




Error-Prevention Tip 3.1








Making the data
members of a class private and the member functions of the class
public facilitates debugging because problems
with data manipulations are localized to either the class's member functions or
the friends of the class.



Member Functions setCourseName and getCourseName


Member
function setCourseName (defined in lines
19–22) does not return any data when it completes its task, so its return type
is void. The member function receives one
parameter—name—which represents the course
name that will be passed to it as an argument (as we'll see in line 55 of
main). Line 21 assigns name to data member
courseName. In this example, setCourseName does not attempt to validate the course name—i.e., the
function does not check that the course name adheres to any particular format or
follows any other rules regarding what a "valid" course name looks like.
Suppose, for instance, that a university can print student transcripts
containing course names of only 25 characters or fewer. In this case, we might
want class GradeBook to ensure that its data member courseName
never contains more than 25 characters. We discuss basic validation techniques
in Section
3.10.


Member function getCourseName (defined in lines 25–28)
returns a particular GradeBook object's courseName. The member function has an empty parameter list, so it
does not require additional data to perform its task. The function specifies
that it returns a string. When a function that
specifies a return type other than void is
called and completes its task, the function returns a result to its calling
function. For example, when you go to an automated teller machine (ATM) and
request your account balance, you expect the ATM to give you back a value that
represents your balance. Similarly, when a statement calls member function
getCourseName on a GradeBook object, the statement expects to
receive the GradeBook's course name (in this case, a string,
as specified by the function's return type). If you have a function
square that returns the square of its argument, the statement


result = square( 2 );


returns 4 from function square and assings to
variable result the value 4. If you have a function
maximum that returns the largest of three integer arguments, the
statement


biggest = maximum( 27, 114, 51 );


returns 114 from function maximum and assigns
to variable biggest the value 114.



Common Programming Error 3.7








Forgetting to
return a value from a function that is supposed to return a value is a
compilation error.



Note that the statements in lines 21
and 27 each use variable courseName (line 39)
even though it was not declared in any of the member functions. We can use
courseName in the member functions of class
GradeBook because courseName is a
data member of the class. Also note that the order in which member functions are
defined does not determine when they are called at execution time. So member
function getCourseName could be defined
before member function setCourseName.


Member Function displayMessage


Member function displayMessage (lines 31–37) does not return any data when it completes
its task, so its return type is void. The
function does not receive parameters, so its parameter list is empty. Lines
35–36 output a welcome message that includes the value of data member
courseName. Line 35 calls member function getCourseName to obtain the value of courseName. Note that member
function displayMessage could also access data member
courseName directly, just as member functions setCourseName
and getCourseName do. We explain shortly why we choose to call member
function getCourseName to obtain the value of
courseName.


Testing Class GradeBook


The main function (lines 43–60) creates one object of
class GradeBook and uses each of its member functions. Line 46 creates
a GradeBook object named myGradeBook. Lines 49–50 display the
initial course name by calling the object's getCourseName member function. Note that the first line of the output
does not show a course name, because the object's courseName data member (i.e., a string) is initially empty—by default, the initial value of a
string is the so-called empty
string
, i.e., a string that does not contain any
characters. Nothing appears on the screen when an empty string is displayed.


Line 53 prompts the user to enter a
course name. Local string variable nameOfCourse (declared in line 45) is set to the course name entered by
the user, which is obtained by the call to the getline function (line
54). Line 55 calls object myGradeBook's setCourseName member
function and supplies nameOfCourse as the
function's argument. When the function is called, the argument's value is copied
to parameter name (line 19) of member function setCourseName (lines 19–22). Then the parameter's value is assigned to data
member courseName (line 21). Line 57 skips a line
in the output; then line 58 calls object myGradeBook's
displayMessage member function to display the welcome message
containing the course name.


Software Engineering with Set and Get
Functions


A class's private data
members can be manipulated only by member functions of that class (and by
"friends" of the class, as we'll see in Chapter
10). So a client of an object—that is, any class or
function that calls the object's member functions from outside the object—calls
the class's public member functions to
request the class's services for particular objects of the class. This is why
the statements in function main (Fig. 3.5, lines
43–60) call member functions setCourseName, getCourseName and
displayMessage on a GradeBook object.
Classes often provide public member
functions to allow clients of the class to set (i.e., assign values to) or get (i.e., obtain the values of)
private data members. The names of these member
functions need not begin with set or get, but this naming convention is common. In this example,
the member function that sets the
courseName data member is called setCourseName, and the member function that gets
the value of the courseName data member is called
getCourseName. Note that set functions
are also sometimes called mutators (because
they mutate, or change, values), and get
functions are also sometimes called accessors (because they access values).


Recall that declaring data members with access specifier
private enforces data hiding. Providing public set and get functions allows clients of a class to access the hidden
data, but only indirectly. The client knows that it is attempting to modify or
obtain an object's data, but the client does not know how the object performs
these operations. In some cases, a class may internally represent a piece of
data one way, but expose that data to clients in a different way. For example,
suppose a Clock class represents the time of day
as a private int data member time that
stores the number of seconds since midnight. However, when a client calls a
Clock object's getTime member
function, the object could return the time with hours, minutes and seconds in a
string in the format "HH:MM:SS". Similarly, suppose the
Clock class provides a set function
named setTime that takes a string parameter in the "HH:MM:SS" format.
Using string capabilities presented in Chapter
18, the setTime function could convert this
string to a number of seconds, which the function stores in its
private data member. The set function could also check that the value it receives
represents a valid time (e.g., "12:30:45" is valid but
"42:85:70" is not). The set and get functions allow a client to
interact with an object, but the object's private
data remains safely encapsulated (i.e., hidden) in the object itself.


The set and get functions of a class
also should be used by other member functions within the class to manipulate the
class's private data, although these
member functions can access the private
data directly. In Fig.
3.5, member functions setCourseName and getCourseName are
public member functions, so they are
accessible to clients of the class, as well as to the class itself. Member
function displayMessage calls member function getCourseName to obtain the value of data member courseName for display purposes, even though
displayMessage can access courseName
directly—accessing a data member via its get function creates a better, more robust class (i.e., a
class that is easier to maintain and less likely to stop working). If we decide
to change the data member courseName in some way, the
displayMessage definition will not require
modification—only the bodies of the get and set functions that directly
manipulate the data member will need to change. For example, suppose we decide
that we want to represent the course name as two separate data
members—courseNumber (e.g., "CS101") and courseTitle
(e.g., "Introduction to C++ Programming"). Member function
displayMessage can still issue a single call to member function
getCourseName to obtain the full course name to
display as part of the welcome message. In this case, getCourseName would need to build and return a string
containing the courseNumber followed by the courseTitle.
Member function displayMessage would continue
to display the complete course title "CS101 Introduction to C++
Programming
," because it is unaffected by the
change to the class's data members. The benefits of calling a set function from another
member function of a class will become clear when we discuss validation in Section
3.10.



Good Programming Practice 3.4








Always try
to localize the effects of changes to a class's data members by accessing and
manipulating the data members through their
get and set functions. Changes to the name of a data member or the data
type used to store a data member then affect only the corresponding
get
and set functions, but
not the callers of those
functions.




Software Engineering Observation 3.3








The class designer need not
provide
set or get functions for each private data item; these capabilities should be provided
only when appropriate. If a service is useful to the client code, that service
should typically be provided in the class's public
interface.



GradeBook's UML Class Diagram with a Data
Member and set and get Functions


Figure
3.6 contains an updated UML class diagram for
the version of class GradeBook in Fig. 3.5. This diagram models
GradeBook's data member courseName as
an attribute in the middle compartment. The UML represents data members as
attributes by listing the attribute name, followed by a colon and the attribute
type. The UML type of attribute courseName is
String, which corresponds to string in C++. Data member
courseName is private in C++, so
the class diagram lists a minus sign () in front of the corresponding attribute's name.
The minus sign in the UML is equivalent to the private access specifier
in C++. Class GradeBook
contains three public member functions, so the
class diagram lists three operations in the third compartment. Recall that the
plus (+) sign before each operation name indicates that the operation is
public in C++. Operation setCourseName has a String
parameter called name. The UML indicates the
return type of an operation by placing a colon and the return type after the
parentheses following the operation name. Member function getCourseName
of class GradeBook (Fig. 3.5) has a string
return type in C++, so the class diagram shows a String return type in
the UML. Note that operations setCourseName and displayMessage
do not return values (i.e., they return void),
so the UML class diagram does not specify a return type after the parentheses of
these operations. The UML does not use void as
C++ does when a function does not return a value.




Fig. 3.6. UML class diagram for class
GradeBook with a private courseName attribute and public
operations setCourseName, getCourseName and
displayMessage.






 


No comments:

Post a Comment