Kuro5hin.org: technology and culture, from the trenches
create account | help/FAQ | contact | links | search | IRC | site news
[ Everything | Diaries | Technology | Science | Culture | Politics | Media | News | Internet | Op-Ed | Fiction | Meta | MLP ]
We need your support: buy an ad | premium membership

[P]
C++ Non-Member Functions Can Improve Encaptualisation

By Morn in MLP
Mon Oct 16, 2000 at 04:51:30 PM EST
Tags: Technology (all tags)
Technology

In this article from the C/C++ Users Journal, Scott Meyers argues that a function that can be implemented as either a class member or as a non-friend non-member should be implemented as a non-friend non-member. He has the (somewhat controversial) view that following this advice improves overall encaptualisation.


The article is rather old (from February - I found it via a recent post in comp.lang.c++.moderated), but I can't find anything about it in a search of Kuro5hin, and I think that it might provoke some interesting discussion.

From my reading of it, his argument is that using non-friend non-member functions instead of member functions increases encaptualisation because it decreases the amount of code which has to be considered for re-write if private class representations change. It's hard to argue with what he says, but something about it feels wrong somehow. Can you convince me otherwise?

Sponsors

Voxel dot net
o Managed Hosting
o VoxCAST Content Delivery
o Raw Infrastructure

Login

Poll
What helps promote encaptualisation most?
o A strict class-based OO language 27%
o A non-class-based OO language 2%
o C-style header based function hiding and grouping 4%
o A C++-style mix-up 0%
o Give me a fully functional language any day! 13%
o This poll is too ill-defined to make any sense 51%

Votes: 68
Results | Other Polls

Related Links
o Kuro5hin
o this article
o C/C++ Users Journal
o Also by Morn


Display: Sort:
C++ Non-Member Functions Can Improve Encaptualisation | 19 comments (13 topical, 6 editorial, 0 hidden)
But naked functions are so un-modular! (1.66 / 3) (#1)
by marlowe on Mon Oct 16, 2000 at 02:54:56 PM EST

They're just out hanging out there in empty space. It would be nice to wrap them up in something so we can organize them. In C+++, I just declare a class with nothing but static members and static methods. It's a kluge, but it's something.

Or maybe a naming convention prefix would do the trick, but that's also ugly.


-- The Americans are the Jews of the 21st century. Only we won't go as quietly to the gas chambers. --
Re: But naked functions are so un-modular! (2.33 / 3) (#5)
by Morn on Mon Oct 16, 2000 at 03:19:08 PM EST

The argument in the article is that they'd go in a namespace associated with the class.

[ Parent ]
Easy solution. (none / 0) (#18)
by pb on Thu Oct 19, 2000 at 05:27:57 AM EST

1) Use Java, they have packages.
2) Don't talk to me about it; I hate packages! ;)

Incidentally, you can make stuff static in C, too, and ensure that it only has file scope. Most of the time this isn't a problem, though; when I want to "organize" something, I use header files. Past that, people build libraries. I've got your "encapsulation" right here!

When you let the compilers do it for you, they do the name-mangling themselves. It isn't any prettier, you just don't have to see it. Maybe if you did have to see it, you wouldn't do it so much. This goes double for the slower, uglier parts of C++, like iostream; it makes printf look downright speedy!
---
"See what the drooling, ravening, flesh-eating hordes^W^W^W^WKuro5hin.org readers have to say."
-- pwhysall
[ Parent ]
namespaces (none / 0) (#19)
by pete23 on Thu Oct 19, 2000 at 09:29:44 AM EST

read up on namespaces. they provide a convenient way of packaging up a module to avoid name clashes.

pete23 - reality on demand
[ Parent ]
Meyer is a heretic! (3.40 / 5) (#6)
by Anonymous 242 on Mon Oct 16, 2000 at 03:41:53 PM EST

Honestly, Meyer is a heretic. This is really the core of the article:
Encapsulation is a means, not an end. There’s nothing inherently desirable about encapsulation. Encapsulation is useful only because it yields other things in our software that we care about.

What Meyer is speaking of is not the one true ivory tower acedmic definition of OO and encapsulation. Meyer is speaking of a down and dirty real world true-to-life definition of OO and encapsulation.

Given the context, Meyer is also correct. For C++, his argument lends itself to cleaner, better code. In some other languages (such as Smalltalk) his argument would lead to a corruption of the OO paradigm.

The converse is also true (4.00 / 4) (#10)
by Pseudonym on Mon Oct 16, 2000 at 08:40:16 PM EST

I agree with Meyers' argument in general, but remember, like all rules of thumb, it breaks down.

In a recent project, something that turned up a lot were operations in certain classes which could have been implemented using only the public interface, but which performed non-trivial computation which later turned out to be bottlenecks in the code. Since the private data in the object didn't change often, the solution was simple: cache the result. And where else should this cache be stored but in the private data of the class?

Had I followed Meyers' advice to begin with, I would have been stuck with a huge refactoring job as I modified every call site from a non-member function call to a method invocation.

This, if you like, is the converse of Meyers' point. His point was about changing functions because of a change in class implementation. But what about changing class implementations because of a function change? My example shows that it does happen and occasionally you have to worry about it.

As a compromise, how about always using a public interface if it will do the job, even if you can access private data or methods? Use discipline instead of compiler checking. At least that would be an improvement...


sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
Amen ... (4.00 / 1) (#12)
by aphrael on Mon Oct 16, 2000 at 11:21:24 PM EST

how about always using a public interface if it will do the job, even if you can access private data or methods? Use discipline instead of compiler checking.

Amen! Compiler checking makes it a little bit easier with multiple-developer teams, but rigorously forcing yourself to use the public interface if you can is a good idea anyway: it can usually help you reduce the size of your public interface as you realize that function (a) can really be expressed in terms of function (c) and (d). Of course, there's a tradeoff there --- but there are usually tradeoffs; the man who says you should always do it his way is probably insane.



[ Parent ]
Seems reasonable ... (3.50 / 4) (#11)
by aphrael on Mon Oct 16, 2000 at 11:19:19 PM EST

In my reading, his argument is a little bit more broad than that --- he's saying if it doesn't need to be a member function, it shouldn't be.

On the surface, that seems reasonable to me --- anything which only interacts with the public interface to a class has no need to be a member, and so shouldn't be. Yet, at the same time, that view of encapsulation only works if you can reasonably guarantee that the function will never need access to class internals in the future; I'm not sure that's always a fair supposition.



Reasonable, but... (4.00 / 1) (#13)
by Bert Peers on Tue Oct 17, 2000 at 04:47:39 AM EST

You'd also run into refactoring problems if the function is to become virtual. "Depending on public data only" vs "functionality dependent on the classtype" are entirely orthogonal so I don't see why this wouldn't happen.

And what about functions that use public *members* (as opposed to public data) ? These can be implemented as nonmembers, but since they use a member, they somehow seem more dependent on the class -- especially if the member they use is virtual.

All in all, it's a bit shaky; traditionally the public/private protection enforced by the compiler is supposed to help the class-user -- the class-builder is supposed to know what he's doing :)

[ Parent ]

Koenig Lookup (4.33 / 3) (#14)
by bgarcia on Tue Oct 17, 2000 at 07:36:06 AM EST

In Exceptional C++, Herb Sutter describes Koenig Lookup (other definitions are available online from a working draft of a C++ standard working draft and from HP developer's resource). This is the algorithm used by C++ compilers to determine which version of a function to call. Roughly, a function is considered to live in the namespace of each of its arguments. I strongly recommend reading items 31 through 34 from Exceptional C++. You can also take a look at the original Guru of the Week article on the subject.

Once you understand, and accept, the fact that the interface of a class is composed of not just its public member functions, but also includes any "free" functions that have that class as an argument, then Meyer's arguments will start to make more sense to you.

It's amazing to find out exactly how much you don't understand about C++ when you start reading about things like this.

Why stop there? (2.00 / 3) (#15)
by daani on Tue Oct 17, 2000 at 09:12:57 AM EST

I think that Meyers' forget that C++ was designed to be extensible and that includes him. To use one of his example:

class Wombat {
...
public:
bool sleep(double hrs);
};

I agree that a that a nap() function (which can be completely implemented in terms of the public interface of Wombat) is better implemented as a non-member function instead of added to the public interface of Wombat. But it would be better still to do a public inherit from Wombat to make a new class and implement nap() there. That way:

* it will be easier in the future to know how much code exists that relies on nap(),
* and it gives more flexibility should the implementation of nap() ever need to change.

Kind of a secod line of encapsulation (look boss defensive designing!!).

class NWombat : public Wombat {
// Data space which may potentially be used later
// to change the implementation of method nap()
public:
bool nap();
};

.






not a good idea (3.50 / 2) (#16)
by pete23 on Tue Oct 17, 2000 at 10:53:12 AM EST

Inheritance is one of the strongest relationships in C++. It creates a very strong coupling, and to my mind is totally inappropriate and unnecessary for the job at hand. See Lakos for a discussion of why reducing coupling is important.

Also, ask yourself what the difference is between Wombat and NWombat - is there enough variance to warrant the amount of work required?

In this case, I'm with Meyers. There's simply no need for nap to be a member of this class.

If I discovered that I had a whole suite of these functions, which presented a simplified or focused view of Wombat (hiding details, like the nap duration) then I'd consider writing an Adaptor class, of course...

pete23 - reality on demand
[ Parent ]
re: not a good idea (3.00 / 1) (#17)
by daani on Tue Oct 17, 2000 at 07:50:35 PM EST

Thanks for the pointers.

In this case, I'm with Meyers. There's simply no need for nap to be a member of this class.

Yes, but where would one place functions that depend on nap()? And if there was a proposal of interface change to nap(), how do we know which code may have to be modified. What I am saying is why do we not encapsulate nap()?

To prevent pollution of the global name-space, maybe class Wombat and class NWombat could be encapsulated within a "WombatStuff" namespace, like Meyers suggested.



[ Parent ]

C++ Non-Member Functions Can Improve Encaptualisation | 19 comments (13 topical, 6 editorial, 0 hidden)
Display: Sort:

kuro5hin.org

[XML]
All trademarks and copyrights on this page are owned by their respective companies. The Rest © 2000 - Present Kuro5hin.org Inc.
See our legalese page for copyright policies. Please also read our Privacy Policy.
Kuro5hin.org is powered by Free Software, including Apache, Perl, and Linux, The Scoop Engine that runs this site is freely available, under the terms of the GPL.
Need some help? Email help@kuro5hin.org.
My heart's the long stairs.

Powered by Scoop create account | help/FAQ | mission | links | search | IRC | YOU choose the stories!