(that's not big news, they're private). |
Oh, you were talking about the private functions? I'm sorry. I had only public functions in mind. Hm, but thinking about it, my policies for making private helpers class members or not are very similar to those for public functions.
1) If they need to be part of the class, e.g. operators, constructors etc, make them. No discussion possible.
2) If they can be implemented without making them friend to the class, do so in the cpp file (in anonymous namespace). Usually, you have to add another parameter holding the this-pointer. (I usually
pass this as reference, though).
3) Else, I use to put them in the class file. (Means: If I would have to make the helper function "friend", I rather implement it directly in the class file).
Now your case would fall into 2). I put rule 2) there mostly because allthough private functions *should* not be part of an interface, they actually are. They can change the class structure, e.g. a virtual function in the private section can make things very different for the whole class. Also, changes in private member signatures forces recompiles. And they can introduce some side effects, when you names clashes with functions defined elsewhere (e.g. in base classes). But that rarely happens in C++ (except you make the functions virtual). For that reasons, I try to clean up classes - even from small private helper functions (if possible). Scott Meyer in his article uses a different base argument: encapsulation. It is also true here, but more about this later.
You have templates.. So you can't implement them in anonymous namespace and can't hide them in a CPP file. Your clients have to recompile on every change anyway and the functions under question aren't virtual. So in your specific case, it might be OK to implement them within the class.
Still.... I would implement them in some "private" namespace, e.g. if your class reside in "namespace tree", you could use "namespace tree::private".
You name two reasons in favour of putting them into the class:
First, you recognize that your implementation helpers have heavy dependencies to your public interface and you want to shortens the distance of both implementations, so they can't be out of sync so easily. Is this right?
Well, I find this a rather weak reason. Actually, I think its even of advantage if the implementation is stown away somewhere else, which would turn your argument upside down. For the danger of getting out of sync, you could put a comment in the class "// implementations are in header splaytreeprivate.h".
The second reason you have seem to be about encapsulation. But honestly, I don't get the exact point you want to make here.
You speak about extensibility and what you would have to change if you introduce a new class. But: If your UberNode public inherits from BaseNode, then your argument is clearly wrong, as all code that applies to BaseNode will still be working with UberNode's (or else, you are violating the Liskov's substitution principle. And than you most probably have a big design flaw in your class hierarchy.)
If it doesn't inherit BaseNode, then I don't see a connection to this problem. It will have its own set of
helper functions then, and you can start deciding again where to put these. Even better - if you make the
utility functions global instead of members, you may be able to reuse them for any other class like UberNode, if functionality is similar. (Do this only, if the similarity is not accidential, but "by design"!)
But all this has nothing to do with encapsulation in the way Scott Meyer uses it. That's about changing
the implementation details of one class and looking at the amount of code you have to verify because of the change. It's not about extending the class hierarchy. And his argument is still an argument in favour of getting private helper functions out of the class (if you can without making them friend). If you change the class implementation detail - say: make the root pointer a smart pointer - you would have to verify the helper functions only, if they are part of the class (as they *could* directly access the new smart pointer (and may have trouble with the change).
I think, most people's argument about this issue is rather an aesthetic one: It just looks better for them, when everything is sorted nicely into one place. This feeling provokes statements like "They are used only by the class, so they
belong to the class, so they should be
part of the class." This logic is not correct. There is no concrete definition of "belong" in this abstraction level (there may be one in UML diagrams ;), so that is rather an empty idea. And I don't see a necessity of something to be part of something else, even if it belong to the other.
The other argument that usually brought up is consistency. If you put some helper (and public functions) inside the class and others outside, users get confused which one belongs to the class and which not and where to find the functions etc.. I think Scott Meyer dismantles this argument nicely: It is already the case. Programmer who think that "everything that belongs to this class must be declared within the class declaration itself" have already a misconception.
Ciao, Imi.
PS:
Aww. actually I am german. I don't know why I say "Ciao" always at the end. Just a habbit ^^