9.9. Access Functions: Getters and Setters

Time: 00:04:42 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides: PDF, PPTX

Review

Making a class's member variables private leads to stronger encapsulation, preventing client code from inadvertently changing or inappropriately relying on them. However, clients may have a legitimate need to retrieve or update private data stored in an object. For example, if the client program uses instances of the Person class, it may need to get the names from the objects or update their addresses when they move. Programmers could satisfy legitimate access needs by making some data public, but doing so would sacrifice encapsulation, exposing the data to both legitimate and illegitimate use. Getter and setter functions (collectively referred to as access functions) satisfy the client's legitimate need to access private data while maintaining encapsulation, and the security offered by the private specification.

Member VariableGetter PrototypesNaming Conventions
int age; int get_age();
int getAge();
  • Derive the function's name from the member whose value the function gets.
  • Form the function name by prepending "get" to the member's name. Use camel case or an underscore when appending the member name.
  • The function's return type is the same as the member's type.
  • The function has no parameters.
string name; string get_name();
string getName();
Getter functions. Getter (aka accessor) functions allow clients to get the current value saved in one of a supplier object's member variables. They are typically small and simple, but their specialized use doesn't limit their complexity. Getters typically return data by value (i.e., by copy) to maintain encapsulation, allowing clients to use the data but not to change the object. Return by value is the default behavior for fundamental types and objects without pointer members. However, getters should copy objects containing pointers or references and return the copy. Alternatively, they can return a const pointer or references (covered in detail later in the chapter).

 

Member VariableSetter PrototypesNaming Conventions
int age; void set_age(int);
void setAge(int);
  • Derive the function's name from the member whose value the function sets.
  • Form the function name by prepending "set" to the member's name. Use camel case or an underscore when appending the member name.
  • The function's return type is void.
  • The function typically has one parameter, the new value saved in the member, whose type is the same as the member's.
string name; void set_name(string);
void setName(string);
Setter functions. Setter functions (aka mutators) allow clients to change the value stored in one of an object's member variables. Whereas clients can change public members without restriction, setters allow class developers to maintain some control over access. Setters may include validity checks, such as ensuring that a person's height or age is non-negative but doesn't exceed a maximum amount. In the case of strings, they can verify that a name has at least one character or reject invalid dates such as "February 31." Setters can also reformat data to match a standard format. For example, if the standard format for a birthday is "1960/12/25," but the user enters the date as "Dec 25, 1960," the setter can reformat the date to match the standard before saving it.

Class developers are not obligated to provide getters and setters for all member variables, but when they do, the functions become a part of the class's public interface. Access functions maintain the separation between the class's implementation and how clients use objects instantiated from it. The following examples demonstrate getters and setters, and how they separate access from implementation.

Access Function Examples

class Person
{
	private:
		string	name;
		int	age;

	public:
		string get_name()
		{
			return name;
		}

		int get_age()
		{
			return age;
		}

		void set_name(string n)
		{
			if (n.length() > 0 && n.length() < 35)
				name = n;
		}

		void set_age(int a_age)
		{
			if (age >= 0 && age < 115)
				age = a_age;
		}
};
Access function examples. The Person class demonstrates basic getter and setter functions. The setter functions perform simple validity checks. The set_name function assumes an arbitrary maximum length, as a database table might impose it. The minimum age set_age requires is appropriate for cultures that begin ages at zero; programmers would modify the test for older traditional cultures that begin counting at one.

 

A picture of a stack implemented as an array. The illustration draws the array vertically, with the first (0th) element at the bottom and the top open. A variable named sp (stack pointer) indexes into the array where the next push operation will place the pushed item. The stack pointer (sp) also represents the size or number of items on the stack. A picture of a stack implemented as a linked list. The picture draws the nodes vertically, connected with arrows representing the links. void push(char);
char pop();
int size();
(a)(b)(c)
Setters and getters: Creating a stable public interface. A stack is an abstract data type (ADT) that stores and retrieves data in a last in, first out (LIFO) order. It provides at least two operations: push stores a new data element in the structure, and pop removes and returns a data element. A third operation, size, is often included and returns the number of data elements currently stored on the stack. The figure illustrates two different stack implementations storing characters (a later chapter demonstrates how to generalize the data type). Although their implementations differ, their public interfaces are identical. The functions are specialized getters and setters that reflect the common ADT practice of naming access functions based on the structure's behavior.
  1. A stack implemented as a zero-indexed array indexed with a stack pointer, sp. The stack pointer indicates the top of the stack - the location of the next push or pop operation - 3 in the illustration. The stack pointer also indicates the stack's size - the number of elements on the stack.
  2. A stack implemented as a linked list. Each square represents a node, divided into two member variables: the bottom rectangle is a pointer to the next node, and the top rectangle holds the data pushed onto the stack. This implementation does not have a stack pointer. Consequently, it must "walk" the list, counting the nodes, or maintain a separate variable that keeps the count as data is pushed onto or popped off the stack.
  3. The prototypes for three C++ member functions implementing the standard stack operations. Although the function bodies depend on the stack's specific implementation, the function signatures (i.e., the class's public interface) do not, allowing the stack developer to change implementations without affecting client code that uses the stack.

Downloadable Access Examples

The main function plays the role of the client in each example. The stack examples utilize simple exception handling, which the text formally introduces later in the chapter.

ViewDownloadComments
access.cpp access.cpp Demonstrates two setters and two getters. Although the members and their types change, the basic access function patterns remain unchanged. The example also demonstrates that when the client changes a value returned by a getter, the object's stored value remains unchanged.
stack1.cpp stack1.cpp A character stack implemented as an array and a stack pointer.
A character stack implemented as a linked list. The top node is a list head that simplifies the push and pop operations by removing the boundary conditions encountered when pushing the first element on the stack or popping the last one off. As such, it does not store data.
stack2.cpp stack2.cpp The size function counts the list nodes each time the program calls it. Although inefficient for large stacks, it demonstrates how separating the data structure's implementation from its interface allows developers to significantly change the implementation without affecting client code.
stack3.cpp stack3.cpp The stack class counts the nodes as they are pushed on or popped off the stack; the size returns the count.

Aside from demonstrating that the array and linked list versions of the stack class can organize and access the stored data in different ways while providing identical interfaces, the actual implementations are irrelevant to the goals of this section. However, for the interested reader, the following figure details the pointer manipulations in the linked-list version of the push operation.

A box representing a node, divided into 'data' and 'down' sections. Each stack node consists of two fields: data and a pointer, down, linking the nodes.
Initial node* temp = new node;
temp->data = c;
temp->down = top->down; top->down = temp;
top points to node without data and a null down pointer. temp points to a new node with data 'A' and down unset. Copies the top node's down, null, to temp node's down. Updates the top node's down to point to temp.
top points to node without data, but its down pointer points to a node storing 'A' in its data field. temp points to a new node with data 'B' and down unset. top's down pointer continues to point to the node storing 'A'. Copies the top node's down, a pointer to the node with 'A', to temp node's down. Updates the top node's down to point to temp.
Linked-list stack: The push operation. The first column illustrates the stack's state at the beginning of the push function. Subsequent columns illustrate how the function's statements change the stack. The pointers top and temp are class and local variables, respectively. The lambda character, λ, denotes a null pointer.