14.1. Private data and classes¶
I have used the word encapsulation in this book to refer to the process of wrapping up a sequence of instructions in a function, in order to separate the function’s interface (how to use it) from its implementation (how it does what it does).
This kind of encapsulation might be called “functional encapsulation,” to distinguish it from “data encapsulation,” which is the topic of this chapter. Data encapsulation is based on the idea that each structure definition should provide a set of functions that apply to the structure, and prevent unrestricted access to the internal representation.
One use of data encapsulation is to hide implementation details from users or programmers that don’t need to know them.
For example, there are many possible representations for a Card
,
including two integers, two strings and two enumerated types. The
programmer who writes the Card
member functions needs to know which
implementation to use, but someone using the Card
structure should
not have to know anything about its internal structure.
As another example, we have been using string
and vector
objects without ever discussing their implementations. There are many
possibilities, but as “clients” of these libraries, we don’t need to
know.
In C++, the most common way to enforce data encapsulation is to prevent
client programs from accessing the instance variables of an object. The
keyword private
is used to protect parts of a structure definition.
For example, we could have written the Card
definition:
struct Card
{
private:
int suit, rank;
public:
Card ();
Card (int s, int r);
int getRank () const { return rank; }
int getSuit () const { return suit; }
void setRank (int r) { rank = r; }
void setSuit (int s) { suit = s; }
};
There are two sections of this definition, a private part and a public
part. The functions are public, which means that they can be invoked by
client programs. The instance variables are private, which means that
they can be read and written only by Card
member functions.
It is still possible for client programs to read and write the instance
variables using the accessor functions (the ones beginning with
get
and set
). On the other hand, it is now easy to control which
operations clients can perform on which instance variables. For example,
it might be a good idea to make cards “read only” so that after they are
constructed, they cannot be changed. To do that, all we have to do is
remove the set
functions.
Another advantage of using accessor functions is that we can change the internal representations of cards without having to change any client programs.
Run the active code below. Uncomment the commented out code to see what happens!
- True
- Incorrect! Data encapsulation should hide implementation details.
- False
- Correct! Data encapsulation prevents unrestricted access to internal representations.
Q-2: Data encapsulation is based on the idea that each structure definition should provide a set of functions that apply to the structure, and allow unrestricted access to the internal representation.
- getSuit
- Correct!
- setRank
- Correct! "Setter" functions are also known as "mutator" functions.
- Incorrect!
- getRank
- Correct!
Q-4: Multiple Response: Which of the following are examples of accessor functions?