I believe in free functions and agree with Sutter, but my understanding is in the opposite direction. It is not that you should have your public methods depend on free functions instead of private methods, but rather that you can build a richer interface outside of the class with free functions by using the provided public interface.
That is, you don't push your privates outside of the class, but rather reduce the public interface to the minimum that allows you to build the rest of the functionality with the least possible coupling: only using the public interface.
In your example, what I would move outside of the class is the CalculateDifference method if it can be represented effectively in terms of other operations.
class Number { // small simple interface: accessor to constant data, constructor
public:
explicit Number( int nNumber ) : m_nNumber( nNumber ) {}
int value() const { return m_nNumber; }
private:
int m_nNumber;
};
Number operator+( Number const & lhs, Number const & rhs ) // Add addition to the interface
{
return Number( lhs.value() + rhs.value() );
}
Number operator-( Number const & lhs, Number const & rhs ) // Add subtraction to the interface
{
return Number( lhs.value() - rhs.value() );
}
The advantage is that if you decide to redefine your Number internals (there is not that much that you can do with such a simple class), as long as you keep your public interface constant then all other functions will work out of the box. Internal implementation details will not force you to redefine all the other methods.
The hard part (not in the simplistic example above) is determining what is the least interface that you must provide. The article (GotW#84), referenced from a previous question here is a great example. If you read it in detail you will find that you can greatly reduce the number of methods in std::basic_string while maintaining the same functionality and performance. The count would come down from 103 member functions to only 32 members. That means that implementation changes in the class will affect only 32 instead of 103 members, and as the interface is kept the 71 free functions that can implement the rest of the functionality in terms of the 32 members will not have to be changed.
That is the important point: it is more encapsulated as you are limiting the impact of implementation changes on the code.
Moving out of the original question, here is a simple example of how using free functions improve the locality of changes to the class. Assume a complex class with really complex addition operation. You could go for it and implement all operator overrides as member functions, or you can just as easily and effectively implement only some of them internally and provide the rest as free functions:
class ReallyComplex
{
public:
ReallyComplex& operator+=( ReallyComplex const & rhs );
};
ReallyComplex operator+( ReallyComplex const & lhs, ReallyComplex const & rhs )
{
ReallyComplex tmp( lhs );
tmp += rhs;
return tmp;
}
It can be easily seen that no matter how the original operator+=
performs its task, the free operator+
performs its duty correctly. Now, with any and all changes to the class, operator+=
will have to be updated, but the external operator+
will be untouched for the rest of its life.
The code above is a common pattern, while usually instead of receiving the lhs
operand by constant reference and creating a temporary object inside, it can be changed so that the parameter is itself a value copy, helping the compiler with some optimizations:
ReallyComplex operator+( ReallyComplex lhs, ReallyComplex const & rhs )
{
lhs += rhs;
return lhs;
}