We have already covered three important types of member functions: constructors, getters, and setters. But when creating a class we will often want to provide other member functions to make the it more useful and useable.
One of the goals for objects is that they encapsulate a particular piece of data - they bundle that data with functions to help work with it. We want to build in as much functionality as is reasonable so that other code can βaskβ the object to perform tasks instead of having to figure out how to do it themselves.
For example, we might know that lots of other code will want to calculate how far a given Point is from the origin. Rather than make client code implement that calculation, it would be a good idea to provide a distanceToOrigin function that returns how far away the point is from the origin (0, 0).
class Point {
public:
...
// Method to get distance from 0,0
double distanceToOrigin() {
return sqrt(m_x * m_x + m_y * m_y);
}
double getX() {
return m_x;
}
double getY() {
return m_y;
}
private:
double m_x, m_y;
};
Or it could use the getters to access those members. Notice that in a member function like distanceToOrigin() we can call another member function like getX() without specifying what Point we want to work with. Outside of the class, we would have to say something like p1.getX() or p2.getX() to specify what point should do the getX() behavior. But if we have already called p1.distanceToOrigin(), then while executing distanceToOrigin() it is assumed that we are working with p1 as our βcurrent pointβ. So in the same way that m_x is understood to mean p1.m_x, getX() is assumed to mean p1.getX():
class Point {
public:
...
// Method to get distance from 0,0
double distanceToOrigin() {
double x = getX(); //call getX on current point
double y = getY(); //call getY on current point
return sqrt(x * x + y * y);
}
double getX() {
return m_x;
}
double getY() {
return m_y;
}
private:
double m_x, m_y;
};
Although member functions can directly access member variables and the first version of distanceToOrigin looks simpler, it can useful to use getters and setters to access the data. This way, if we later decide to change the implementation of the class, we have less code to fix. Say we decided to switch to storing the point with polar coordinates. In the first version we would have to rewrite all three shown functions. In the second version, we would have to rewrite getX and getY but then could leave distanceToOrigin or other functions alone if we wanted. (Though in this case it would make sense to change the function as the new version could avoid doing any computations).
A call to a function does involve a very tiny cost in terms of machine level instructions to be executed. But compilers can often optimize away these costs - there is a good chance that a compiler will turn something like getX() into a direct use of the correct value in memory. For most projects, you are much better off optimizing code for readability and maintainability instead of trying to optimize for low level behavior.
What other functions might we want to add to a Point class? It depends on how we anticipate points will be used. Some possible examples might be a function to shift a point or one to get a string representation of the point: