C++ – How Non-Member Functions Improve Encapsulation

cfunction

I read Scott Meyers' article on the subject and quite confused about what he is talking about. I have 3 questions here.

Question 1

To explain in detail, assume I am writing a simple vector<T> class with methods like push_back, insert and operator []. If I followed Meyers' algorithm, I would end up with all non-member friend functions. I will have a vector class with few private members and many non-member friend functions. Is this what he is talking about?

Question 2

I am still not understanding how non-member functions improve encapsulation. Consider the code given in Meyers' article.

class Point {
public:
   int getXValue() const; 
   int getYValue() const; 
   void setXValue(int newXValue);
   void setYValue(int newYValue);

private:
  ...                 // whatever...
};

If his algorithm is followed, setXXXX methods should be non-members. My question is how that increases encapsulation? He also says

We've now seen that a reasonable way
to gauge the amount of encapsulation
in a class is to count the number of
functions that might be broken if the
class's implementation changes.

Until we keep the method signature intact when class implementation changes, no client code is gonna break and it is well encapsulated, right? The same applies for non-member functions as well. So what is the advantage non-member function provides?

Question 3

Quoting his algorithm

else if (f needs type conversions
         on its left-most argument)
   {
   make f a non-member function;
   if (f needs access to non-public
       members of C)
      make f a friend of C;
   }

What he meant by f needs type conversions on its left-most argument?
He also says the following in the article.

Furthermore, we now see that the
common claim that "friend functions
violate encapsulation" is not quite
true. Friends don't violate
encapsulation, they just decrease it —
in exactly the same manner as a member
functions.

This and the above algorithm are contradictory, right?

Best Answer

Question 1

In this case, following Meyers's algorithm will give you member functions:

  • Do they need to be virtual? No.
  • Are they operator<< or operator>>? No.
  • Do they need type conversions? No.
  • Can they be implemented in terms of the public interface? No.
  • So make them members.

His advice is to only make them friends when they really need to be; to favour non-member non-friends over members over friends.

Question 2

The SetXXXX functions need to access the internal (private) representation of the class, so they can't be non-member non-friends; so, Meyers argues, they should be members rather than friends.

The encapsulation comes about by hiding the details of how the class is implemented; you define a public interface separately from a private implementation. If you then invent a better implementation, you can change it without changing the public interface, and any code using the class will continue to work. So Meyers's "number of functions which might be broken" counts the member and friend functions (which we can easily track down by looking at the class definition), but not any non-member non-friend functions using the class through its public interface.

Question 3

This has been answered.

The important points to take away from Meyers's advice are:

  • Design classes to have clean, stable public interfaces separate from their private implementation;
  • Only make functions members or friends when they really need to access the implementation;
  • Only make functions friends when they can't be members.
Related Topic