In the (otherwise) excellent book C++ Coding Standards, Item 44, titled "Prefer writing nonmember nonfriend functions", Sutter and Alexandrescu recommend that only functions that really need access to the members of a class be themselves members of that class. All other operations which can be written by using only member functions should not be part of the class. They should be nonmembers and nonfriends. The arguments are that:
- It promotes encapsulation, because there is less code that needs access to the internals of a class.
- It makes writing function templates easier, because you don't have to guess each time whether some function is a member or not.
- It keeps the class small, which in turn makes it easier to test and maintain.
Although I see the value in these argument, I see a huge drawback: my IDE can't help me find these functions! Whenever I have an object of some kind, and I want to see what operations are available on it, I can't just type "
pMysteriousObject->" and get a list of member functions anymore.
Keeping a clean design is in the end about making your programming life easier. But this would actually make mine much harder.
So I'm wondering if it's really worth the trouble. How do you deal with that?
How to read until EOF from cin in C++
Is there a convenient way to wrap std::pair as a new type?
Parsing iCal/vCal/Google calendar files in C++
I think if the behavior of function
foo()falls within the realm of class
Bar's responsibilities, then
foo()should be part of
Can the result of a function call be used as a default parameter value?
The fact that
foo()doesn't need direct access to
Bar's member data doesn't mean it isn't conceptually part of
C++ “Named Parameter Idiom” vs. Boost::Parameter library
It can also mean that the code is well factored.
C++ headers - separation between interface and implementation details
It's not uncommon to have member functions which perform all their behavior via other member functions, and I don't see why it should be..
Fast library to replace CDC vector graphics
I fully agree that peripherally-related functions should not be part of the class, but if something is core to the class responsibilities, there's no reason it shouldn't be a member, regardless of whether it is directly mucking around with the member data..
How to get folder sharing on Windows Mobile emulator to work
As for these specific points:.
It promotes encapsulation, because there is less code that needs access to the internals of a class..Indeed, the fewer functions that directly access the internals, the better.
That means that having member functions do as much as possible via other member functions is a good thing.
Splitting well-factored functions out of the class just leaves you with a half-class, that requires a bunch of external functions to be useful.
Pulling well-factored functions away from their classes also seems to discourage the writing of well-factored functions..
It makes writing function templates easier, because you don't have to guess each time whether some function is a member or not..I don't understand this at all.
If you pull a bunch of functions out of classes, you've thrust more responsibility onto function templates.
They are forced to assume that even less functionality is provided by their class template arguments, unless we are going to assume that most functions pulled from their classes is going to be converted into a template (ugh)..
It keeps the class small, which in turn makes it easier to test and maintain..Um, sure.
It also creates a lot of additional, external functions to test and maintain.
I fail to see the value in this..
The "best" interface for a class can be found only by balancing many competing concerns, of which the degree of encapsulation is but one.". If a function would be something that "just makes sense" to be a member function, make it one.
Likewise, if it isn't really part of the main interface, and "just makes sense" to be a non-member, do that.. One note is that with overloaded versions of eg operator==(), the syntax stays the same.
So in this case you have no reason not to make it a non-member non-friend floating function declared in the same place as the class, unless it really needs access to private members (in my experience it rarely will).
And even then you can define operator!=() a non-member and in terms of operator==()..
If a utility function has two independent classes as parameteres, which class should "own" the member function?.Another issue, is you can only add member functions where the class in question is under your control.
Any helper functions that you write for std::string will have to be non-members since you cannot re-open the class definition.. For both of these examples, your IDE will provide incomplete information, and you will have to use the "old fashion way".. Given that the most influential C++ experts in the world consider that non-member functions with a class parameter are part of the classes interface, this is more of an issue with your IDE rather than the coding style.. Your IDE will likely change in a release or two, and you may even be able to get them to add this feature.
If you change your coding style to suit todays IDE you may well find that you have bigger problems in the future with unextendable/unmaintainable code..
In theory, your class should only contain the data and expose the interface for what it is intended and not utilitarian functions.
Adding utility functions to the interface just grow the class code base and make it less maintainable.
I currently maintain a class with around 50 public methods, that's just insane.. Now, in reality, I agree that this is not easy to enforce.
It's often easier to just add another method to your class, even more if you are using an IDE that can really simply add a new method to an existing class.. In order to keep my classes simple and still be able to centralize external function, I often use utility class that works with my class, or even namespaces.
I start by creating the class that will wrap my data and expose the simplest possible interface.
I then create a new class for every task I have to do with the class.
. Example: create a class Point, then add a class PointDrawer to draw it to a bitmap, PointSerializer to save it, etc..
When I write a method, I like to pick good parents - a fitting class - where they have the best chances to feel welcome and help..