Non-member vs. Member

U

utab

Dear all,

Is there a clear distinction how to decide which functions to be
members of a class and which not

How is your attitude (Your general way from your experiences ...)

"If the function changes the state of the object, it ought to be a
member of that object." Reference Accelerated C++, A. Koenig, page 159.

Thx to you all.
 
A

Andrew Koenig

"If the function changes the state of the object, it ought to be a
member of that object." Reference Accelerated C++, A. Koenig, page 159.

Please note that "ought to be" is less strong than "should always be." It
means "make it a member unless you have a good reason to do otherwise."

One such reason might be a function that changes the state of two or more
objects of different classes. Such a function cannot possibly be members of
both objects. Moreover, such functions are not uncommon: operator>> is
probably the most widely used example.

So sometimes it's impossible to make state-changing functions members of
their objects. Still, it's nice to do so when there is a choice.
 
B

BigBrian

utab said:
Dear all,

Is there a clear distinction how to decide which functions to be
members of a class and which not

This isn't a C++ question....
How is your attitude (Your general way from your experiences ...)

"If the function changes the state of the object, it ought to be a
member of that object." Reference Accelerated C++, A. Koenig, page 159.

I'm sure there are examples where this doesn't make since, but in
general I agree.
 
D

Daniel T.

"Andrew Koenig said:
Please note that "ought to be" is less strong than "should always be." It
means "make it a member unless you have a good reason to do otherwise."

One such reason might be a function that changes the state of two or more
objects of different classes. Such a function cannot possibly be members of
both objects. Moreover, such functions are not uncommon: operator>> is
probably the most widely used example.

So sometimes it's impossible to make state-changing functions members of
their objects. Still, it's nice to do so when there is a choice.

I would go so far as to say that if the function changes the state of
two or more objects, it should not be in the class, even if both/all
objects are of the same type.
 
P

Phlip

BigBrian said:
This isn't a C++ question....

Mr. Manners reminds the Gentle Poster that replies should be on topic if
possible.

For example, C++ brings many special considerations to the question of
whether a function should be semantically a member of an object's
interface, or syntactically a member of its class. operator<<, as already
mention, is semantically a member of both the interfaces of your object and
of ostream. But it is syntactically not a member of your class's methods.
 
V

Victor Bazarov

Daniel said:
I would go so far as to say that if the function changes the state of
two or more objects, it should not be in the class, even if both/all
objects are of the same type.

Actually, I strongly disagree. If it only concerns this particular type,
then the function _should_ be in the class. It should probably be static
of course.

V
 
C

Cy Edmunds

utab said:
Dear all,

Is there a clear distinction how to decide which functions to be
members of a class and which not

How is your attitude (Your general way from your experiences ...)

"If the function changes the state of the object, it ought to be a
member of that object." Reference Accelerated C++, A. Koenig, page 159.

Thx to you all.

Koenig's quote notwithstanding, there is an undesirable aspect of member
functions: they can depend on the implementation. This is sometimes called
"breaking encapsulation". This tends to reduce the maintainability of the
code because if the implementation changes there is more to do and more
mistakes to make. Consider:

void
SomeClass::notify_and_print()
{
notify(); // member function
print(); // another member function;
}

Classes like this which can be written entirely in terms of the rest of an
object's interface can be refactored out:

void
notify_and_print(SomeClass &c)
{
c.notify();
c.print();
}

Although both of these functions will continue to work if the implementation
changes, in the second case I KNOW this to be the case because the second
function has no access to the private parts of SomeClass (i.e. doesn't
"break encapsulation").

As a result of these considerations I usually don't allow member functions
which feature gratuitious access to the implementation. However, I certainly
make exceptions if the interface design seems to call for it.

Cy
 
D

Daniel T.

"Victor Bazarov said:
Actually, I strongly disagree. If it only concerns this particular type,
then the function _should_ be in the class. It should probably be static
of course.

There are readability/writability factors involved. If the function is a
static class member, then it helps scope the function, although the
parameters do that so humm... why should it be static in the class?

As an example:

class Object {
public:
// whatever
};

// returns true if 'a' and 'b' have intersecting areas.
bool hit( Object& a, Object& b );

Why exactly _should_ 'hit' be a static in Object?
 
V

Victor Bazarov

Daniel said:
There are readability/writability factors involved. If the function
is a static class member, then it helps scope the function, although
the parameters do that so humm... why should it be static in the
class?

As an example:

class Object {
public:
// whatever
};

// returns true if 'a' and 'b' have intersecting areas.
bool hit( Object& a, Object& b );

Why exactly _should_ 'hit' be a static in Object?

Simply because when I see

hit(a, b);

I really have no idea what 'a' and 'b' are and which of fourteen 'hit'
functions is going to be used. If I see

Object::hit(a, b)

there is no doubt which function is used.

Another argument: limiting the name scope is impossible with arguments.
And limiting the scope of every name is what we should be doing all the
time. Polluting namespace scope with functions that do something very
specific to objects of a single class is... well, polluting.

V
 
D

Daniel T.

"Victor Bazarov said:
Simply because when I see

hit(a, b);

I really have no idea what 'a' and 'b' are and which of fourteen 'hit'
functions is going to be used.

Seems to me that if you don't know what 'a' and 'b' are, you really
shouldn't be writing the code.

If I see

Object::hit(a, b)

there is no doubt which function is used.

The above is nothing more than a compiler enforced version of hungarian
notation.

Another argument: limiting the name scope is impossible with arguments.
And limiting the scope of every name is what we should be doing all the
time. Polluting namespace scope with functions that do something very
specific to objects of a single class is... well, polluting.

Unless, of course, the namespace is for "things that work with Objects"
presumably...
 
I

I V

Cy said:
As a result of these considerations I usually don't allow member functions
which feature gratuitious access to the implementation. However, I certainly

This has the disadvantage that the way in which the function is called
depends on whether or not it needs to access the implementation, i.e.

o.needs_acess();

vs,

doesnt_need_access(o);

This kind of breaks encapsulation too, as the interface of the class
depends on implementation details, namely, whether or not a particular
function requires access to the internals of the class. To get round
this, I've sometimes considered writing classes with no public member
functions at all. I've never actually written a whole program like
this, because it's sufficiently odd that it might constitute a
stumbling block for anyone else reading the code, but I'd be interested
in what others think of the idea. E.g:

class MyClass
{
private:
SomeClass data_;

virtual void do_operation();
friend void needs_access();


public:
explicit MyClass(SomeClass& d)
: data_(d)
{ }

virtual ~MyClass();
};

void needs_access(MyClass& c)
{
...
}

void doesnt_need_access(MyClass& c)
{
...
}

void do_operation(MyClass& c)
{
c.do_operation();
}
 
C

Cy Edmunds

I V said:
This has the disadvantage that the way in which the function is called
depends on whether or not it needs to access the implementation, i.e.

o.needs_acess();

vs,

doesnt_need_access(o);

This is true but sort of unavoidable. Sooner or later you will want to get
some sort of extended functionality and you only have two choices: derive a
new class with the member function you want, or just write another function:

didnt_think_of_this_at_first(o);

Deriving a new class just to get a new function is often a poor idea for a
lot of reasons. For example:

my_string : public std::string
{
public:
void my_leet_function();
};

Yech.
This kind of breaks encapsulation too, as the interface of the class
depends on implementation details, namely, whether or not a particular
function requires access to the internals of the class.

This is certainly not what I would call breaking encapsulation. Actually I
don't see the problem here at all.

To get round
this, I've sometimes considered writing classes with no public member
functions at all. I've never actually written a whole program like
this, because it's sufficiently odd that it might constitute a
stumbling block for anyone else reading the code, but I'd be interested
in what others think of the idea. E.g:

class MyClass
{
private:
SomeClass data_;

virtual void do_operation();
friend void needs_access();


public:
explicit MyClass(SomeClass& d)
: data_(d)
{ }

virtual ~MyClass();
};

void needs_access(MyClass& c)
{
...
}

void doesnt_need_access(MyClass& c)
{
...
}

void do_operation(MyClass& c)
{
c.do_operation();
}

I think it would be a stumbling block for anyone else reading the code!
Also, if you have some reason to try to hide which things need access to the
internals and which things don't, this is hardly a solution.

Cy
 
C

Cy Edmunds

Victor Bazarov said:
Simply because when I see

hit(a, b);

I really have no idea what 'a' and 'b' are and which of fourteen 'hit'
functions is going to be used. If I see

Object::hit(a, b)

there is no doubt which function is used.

I think this argument is a giant stretch but even if I were to buy it what
if you later need some other functionality? You can always write your own

bool miss( Object& a, Object& b );

but you may not be able to add you own static function. That's why I favor
making the interface as simple as possible and using stand alone functions,
overloaded if necessary.

If you really want to insist on qualifiers you can use namespaces:

namespace dubious
{
bool miss( Object& a, Object& b );
}

dubious::miss(a, b);

Kinda dumb, but at least it is extendable.
 
V

Victor Bazarov

Cy said:
[...]
If you really want to insist on qualifiers you can use namespaces:

namespace dubious
{
bool miss( Object& a, Object& b );
}

dubious::miss(a, b);

Kinda dumb, but at least it is extendable.

No more and no less than classes. I can always wrap one class in another
to "add functionality" AFA static members are concerned.

Classes are not namespaces and namespaces are not classes. Substituting
one for the other just for the sake of substituting is pointless. Any
functionality specific to a class belongs to the class.

V
 
C

Cy Edmunds

Victor Bazarov said:
Cy said:
[...]
If you really want to insist on qualifiers you can use namespaces:

namespace dubious
{
bool miss( Object& a, Object& b );
}

dubious::miss(a, b);

Kinda dumb, but at least it is extendable.

No more and no less than classes. I can always wrap one class in another
to "add functionality" AFA static members are concerned.

Classes are not namespaces and namespaces are not classes. Substituting
one for the other just for the sake of substituting is pointless. Any
functionality specific to a class belongs to the class.

I agree with everything you said except the last sentence. In C++ there is
no reason to break encapsulation just because you want to add some
functionality.

Cy
 
V

Victor Bazarov

Cy said:
[..] In C++
there is no reason to break encapsulation just because you want to
add some functionality.

I've read that expression several times today, and still have not
understood how putting functionality that specific to any particular
class inside that class means "breaking encapsulation". Care to
elaborate?

V
 
D

Daniel T.

"utab said:
Dear all,

Is there a clear distinction how to decide which functions to be
members of a class and which not

How is your attitude (Your general way from your experiences ...)

"If the function changes the state of the object, it ought to be a
member of that object." Reference Accelerated C++, A. Koenig, page 159.

An important point that I think needs to be mentioned. No
member-function should return with the class invariant broken (however
it can break the invariant inside itself.)

So for example:

class Range {
// invariant: low() < high()
public:
int low() const;
int high() const;
void setLow( int v );
// postcondition: low() == v
};

The 'setLow' member-function above has a real problem, it can't
guarantee it's postcondition and its invariant unless the user of the
class first checks to see if the value passed in is less than 'high()'.

Many new programmers, will ignore this glaring problem, some will add an
assert( v < high() ) or throw and exception if v >= high() and think
they are clever, but neither "solution" really solves the problem. The
user of the class must still do most of the work ensuring the invariant
stays true (and that is supposed to be the class' responsibility.)

There are only two solutions, either 'setLow's precondition needs to
change to something that works [ like: low() == min( v, high() - 1 ) ],
or the function needs to go away.

To generalize this rule. Anytime a member-function *requires* the object
to be in a particular state in order to be called, the member-function
is poorly formed and needs to be fixed or removed.
 
I

I V

Cy said:
This is true but sort of unavoidable. Sooner or later you will want to get
some sort of extended functionality and you only have two choices: derive a
new class with the member function you want, or just write another function:

Well, that makes me wonder if having a special syntax for calling
member functions is actually a good thing. After all, why should it
matter to the user of the class if the operation on an object is
defined by the class or by some later code? Combining the idea of
"requires access to class internals," "is part of the class's
interface" and "is an operation on objects of the class" into one
syntax is, perhaps, confusing.

I belive in the Dylan language, obj.meth(arg) is just syntactic sugar
for meth(obj, arg) , which strikes me as quite an attractive design (in
Dylan, you define a function which does dynamic dispatch on the types
of its arguments outside of a class definition).
This is certainly not what I would call breaking encapsulation. Actually I
don't see the problem here at all.

Whether a function needs access to the internals of a class depends on
the implementation, no? A maybe interesting example is a class with two
methods, each of which can be implemented in terms of the other; thus,
which you make access the class internals is a matter of choice. If you
make one a member function and one a non-member, you're exposing this
choice in the interface, which sounds like breaking encapsulation to
me. Certainly, if you later decide to change your implementation
(perhaps implementing function A in terms of function B turns out to be
more efficient than the other way round), making one a member and the
other a non-member will cause you problems.
 
T

Tom Widmer

Victor said:
Cy said:
[..] In C++
there is no reason to break encapsulation just because you want to
add some functionality.


I've read that expression several times today, and still have not
understood how putting functionality that specific to any particular
class inside that class means "breaking encapsulation". Care to
elaborate?

If you add functionality to a class via (non-friend) non-members, then
that functionality necessarily only depends on the public interface of
the class.

Tom
 
V

Victor Bazarov

Tom said:
Victor said:
Cy said:
[..] In C++
there is no reason to break encapsulation just because you want to
add some functionality.


I've read that expression several times today, and still have not
understood how putting functionality that specific to any particular
class inside that class means "breaking encapsulation". Care to
elaborate?

If you add functionality to a class via (non-friend) non-members, then
that functionality necessarily only depends on the public interface of
the class.

Yes, and?... Where is the "breaking" part? If the functionality is
instead actually _part_ of public interface, what's broken? Or is adding
to a class' interface in fact "breaking" some fragile "encapsulation"?

What is the essential difference between

class A {
// some interface, public or otherwise
};

void foo(A& a) {
// some implemenation
}

and

class A {
// some interface, public or otherwise
static void foo(A& a) {
// same implemenation as above
}
};

What is broken in the second case compared to the first one?

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top