As we discussed, classes typically make their data members private, and private members can not be directly accessed by the public.
Consider the following Date
class:
#include <iostream>
class Date
{
private:
int m_year{ 2020 };
int m_month{ 10 };
int m_day{ 14 };
public:
void print() const
{
std::cout << m_year << '/' << m_month << '/' << m_day << '\n';
}
};
int main()
{
Date d{}; // create a Date object
d.print(); // print the date
return 0;
}
While this class provides a print()
member function to print the entire date, this may not be sufficient for what the user wants to do. For example, what is the user of a Date
object wanted to get the year, or to change the year to a different value. They would be unable to do so, as m_year
is private (and thus can't be directly accessed by the public).
Access Functions
An access function is a trivial public member function whose job is to retrieve or change the value of a private member variable.
Access functions come in two flavors: getters and setters. Getters (also sometimes called accessors) are public member functions that return the value of a private member variable. Setters (also sometimes called mutators) are public member functions that set of a private member variable.
Getters are usually made const, so they can be called on both const and non-const objects. Setters should be non-const, so they can modify the data members.
Let's update our Date
class to have a full set of getters and setters:
#include <iostream>
class Date
{
private:
int m_year { 2020 };
int m_month { 10 };
int m_day { 14 };
public:
void print()
{
std::cout << m_year << '/' << m_month << '/' << m_day << '\n';
}
int getYear() const { return m_year; } // getter for year
void setYear(int year) { m_year = year; } // setter for year
int getMonth() const { return m_month; } // getter for month
void setMonth(int month) { m_month = month; } // setter for month
int getDay() const { return m_day; } // getter for day
void setDay(int day) { m_day = day; } // setter for day
};
int main()
{
Date d{};
d.setYear(2021);
std::cout << "The year is: " << d.getYear() << '\n';
return 0;
}
Access function naming
There is no common convention for naming access functions. However, there are few naming conventions that are more popular than others.
- Prefixed with “get” and “set”:
int getDay() const { return m_day; } // getter
void setDay(int day) { m_day = day; } // setter
The advantage of using “get” and “set” prefixes is that it makes it clear that these are access functions.
- No prefix:
int day() const { return m_day; } // getter
void day(int day) { m_day = day; } // setter
The style is more concise, and uses the same name for both the getter and setter (relying on function overloading to differentiate the two). The C++ standard library uses this convention.
The downside of the no-prefix convention is that it is not particularly obvious that this is setting the value of the day member:
d.day(5); // does this look like it's setting the day member to 5?
- “set” prefix only:
int day() const { return m_day; } // getter
void setDay(int day) { m_day = day; } // setter
Getters should return by value or by const lvalue reference
Getters should provide “read-only” access to data. Therefore, the best practice is that they should return by either value (if making a copy of the member is inexpensive) or by const lvalue reference.