Herb Sutter has said that the most object oriented way to write methods in C++ is using non-member non-friend functions. Should that mean that I should take private methods and turn them into non-member non-friend functions? Any member variables that these methods may need can be passed in as parameters.
Example (before):
class Number {
public:
Number( int nNumber ) : m_nNumber( nNumber ) {}
int CalculateDifference( int nNumber ) { return minus( nNumber ); }
private:
int minus( int nNumber ) { return m_nNumber - nNumber; }
int m_nNumber;
};
Example (after):
int minus( int nLhsNumber, int nRhsNumber ) { return nLhsNumber - nRhsNumber; }
class Number {
public:
Number( int nNumber ) : m_nNumber( nNumber ) {}
int CalculateDifference( int nNumber ) { return minus( m_nNumber, nNumber ); }
private:
int m_nNumber;
};
Am I on the right track? Should all private methods be moved to non-member non-friend functions? What should be rules that would tell you otherwise?
A member function is declared in the class but defined outside the class and is called using the object of the class. A non-member function that is declared outside the class but called a normal function inside the main function.
A friend function is a function that isn't a member of a class but has access to the class's private and protected members. Friend functions aren't considered class members; they're normal external functions that are given special access privileges.
A member function is a part of any class in which it is declared. A friend function can be declared in private, public or protected scope of the class without any effect.
Non-member functions are commonly used when the developer of a library wants to write binary operators that can be overloaded on either argument with a class type, since if you make them a member of the class you can only overload on the second argument (the first is implicitly an object of that class).
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With