Non-member vs. Member

C

Cy Edmunds

Victor Bazarov said:
Tom said:
Victor said:
Cy Edmunds wrote:

[..] 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

In the second case a mistake in the implementation of foo can result in
violation of invariants of the class.
 
V

Victor Bazarov

Cy said:
Victor Bazarov said:
Tom said:
Victor Bazarov wrote:
Cy Edmunds wrote:

[..] 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

In the second case a mistake in the implementation of foo can result
in violation of invariants of the class.

Yes, I agree. Is that the whole thing or there's something else?

If that's the whole thing, then in my book the benefits of limiting the
scope of functionality outweigh the benefits of preventing my potential
mistakes while implementing 'foo'. *I* do not make any. The users of
my classes OTOH do. And they make mistakes mostly because of an unclear
intentions, communicated poorly by placing functionality in overly broad
scopes.

V
 
C

Cy Edmunds

I V said:
Well, that makes me wonder if having a special syntax for calling
member functions is actually a good thing.

You certainly have a good point there. Still, if we are going to program in
C++ we need to deal with it as it actually is.

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).


Whether a function needs access to the internals of a class depends on
the implementation, no?

No, only the interface. For instance:

class IComplex
{
public:
virtual double re() const = 0;
virtual double im() const = 0;
virtual ~IComplex() {}
};

double mod(IComplex const &c)
{
return c.re()*c.re() + c.im()*c.im();
}

I can tell that mod does not require access to the internals from the
interface alone. Of course I can't tell if IComplex::re() or IComplex::im()
actually require access to any internals (they might just generate random
numbers for instance). We can only say that these functions have access to
the internal representation which makes them dangerous -- in a bad
implementation they might break invariants promised by the class
documentation in a way mod could never do.

A maybe interesting example is a class with two
 
T

Tom Widmer

Victor said:
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?

The number of bits of code that need to be checked when the
implementation of A changes.

Tom
 
D

Daniel T.

"Victor Bazarov said:
Yes, I agree. Is that the whole thing or there's something else?

If that's the whole thing, then in my book the benefits of limiting the
scope of functionality outweigh the benefits of preventing my potential
mistakes while implementing 'foo'. *I* do not make any. The users of
my classes OTOH do. And they make mistakes mostly because of an unclear
intentions, communicated poorly by placing functionality in overly broad
scopes.

How exactly have you changed the scope of foo by putting it in the class
rather than in out of it? I missed that part. Seems to me that in both
examples, the scope of foo is the same, only the name has changed.
 
D

Daniel T.

"Victor Bazarov said:
Daniel said:
[..] Seems to me [..]

Sorry, I'm bailing out. I got bored by this nonsense.

I'm not saying your wrong Victor, I'm just trying to figure out why you
are so sure you are right...

Please, explain to me how making a function a public static member of a
class gives it a smaller scope than one that is free standing.
 
V

Victor Bazarov

Daniel said:
Victor Bazarov said:
Daniel said:
[..] Seems to me [..]

Sorry, I'm bailing out. I got bored by this nonsense.

I'm not saying your wrong Victor, I'm just trying to figure out why
you are so sure you are right...

Please, explain to me how making a function a public static member of
a class gives it a smaller scope than one that is free standing.

I don't understand the question, probably. The scope of a member function
is the class of which it's the member. That's smaller than the enclosing
[namespace or other type] scope. I thought that was obvious.

Anyway, I am marking this thread "ignore". Trick me into participating in
a thread from which I bailed out, shame on you. Trick me twice...

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.

Here is what Scott Meyers has to say:

http://www.ddj.com/dept/cpp/184401197
 
T

Tom Widmer

Victor said:
Tom Widmer wrote:


Please elaborate. Thanks.

Specifically, foo needs to be checked only in case 2 if the
implementation of A later changes. In case 1, the amount of code that
may depend on the implementation of A (and must be examined to determine
this) is less.

Tom
 
D

Daniel T.

"Victor Bazarov said:
The scope of a member function
is the class of which it's the member. That's smaller than the enclosing
[namespace or other type] scope. I thought that was obvious.

I thought the "scope" of a variable or function had something to do with
where it can be used in the program. Am I wrong?

In the following code:

class Foo {
public:
static int bar();
};

int bar();

does "Foo::bar()" have a smaller scope than "bar()" or not? If it does,
what does "scope" mean?

Victor says that Foo::bar() has a smaller scope and I tend to respect
his opinion (if not his somewhat abrasive demeanor.)
 
T

Tom Widmer

Daniel said:
The scope of a member function
is the class of which it's the member. That's smaller than the enclosing
[namespace or other type] scope. I thought that was obvious.


I thought the "scope" of a variable or function had something to do with
where it can be used in the program. Am I wrong?
Yes.


In the following code:

class Foo {
public:
static int bar();
};

int bar();

does "Foo::bar()" have a smaller scope than "bar()" or not? If it does,
what does "scope" mean?

The scope is the declarative region of the program in which the name (in
unqualified form, importantly) is valid.
Victor says that Foo::bar() has a smaller scope and I tend to respect
his opinion (if not his somewhat abrasive demeanor.)

Victor is right here, but his abrasive demeanor is often used to
disguise ignorance or mistakes. He's not reading anyway apparently. ;)

In this case he appears to want to put the function into class scope to
avoid name clashes. However, an alternative (and more flexible) way of
doing this is to use namespaces.

Tom
 
K

Kaz Kylheku

Daniel said:
In the following code:

class Foo {
public:
static int bar();
};

int bar();

does "Foo::bar()" have a smaller scope than "bar()" or not? If it does,
what does "scope" mean?

Victor says that Foo::bar() has a smaller scope and I tend to respect
his opinion (if not his somewhat abrasive demeanor.)

If one scope is contained entirely within another, then it can be said
to be smaller. For other cases, you need to develop some notion of
scope size. Even if there is containment, you could argue that any
shadowed pieces of the outer scope do not contribute to its size, thus
casting doubt on the validity of using containment to determine
relative size.

The scope of the name of the non-member function bar extends from its
point of declaration to the end of the translation unit. (That scope
may be interrupted in places by shadowing definitions of bar).

The scope of Foo::bar() is the class scope of Foo. Within a translation
unit, that scope is the class definition, and also the bodies of any
subsequent member function definitions.

In the above example, the Foo class declaration is placed in a region
of program text where the file scope bar() isn't visible yet. The two
scopes do not overlap, and we cannot say which is larger.
 
N

Noah Roberts

Tom said:
In this case he appears to want to put the function into class scope to
avoid name clashes. However, an alternative (and more flexible) way of
doing this is to use namespaces.

Is it in fact true that an alternative is namespaces? I think Victor's
point was that putting it in a class makes it so it can't be
automatically resolved. I am not completely sure when and where
functions can be pulled out of namespace scope into the current scope
but I do know there are cases when this can occur (One of the red books
explains in some detail). I don't believe this can happen if the
function is a static class member.
 
T

Tom Widmer

Noah said:
Tom Widmer wrote:




Is it in fact true that an alternative is namespaces? I think Victor's
point was that putting it in a class makes it so it can't be
automatically resolved. I am not completely sure when and where
functions can be pulled out of namespace scope into the current scope
but I do know there are cases when this can occur (One of the red books
explains in some detail). I don't believe this can happen if the
function is a static class member.

Yes, ADL allows unqualified names from other namespaces to be considered
in function calls. But you can qualify function calls to prevent this.
Quite a common practice is to use quite small namespaces (in contrast to
the monolithic namespace std), that only contain a class and its related
functions.

Anyway, I disagree with Victor on this. If one adds every little
function as an extra static member of a class, one ends up with reams of
code that need to be checked and possibly changed when you change the
implementation. Imagine, for example, a string class. Imagine that you
have lots of utility string operations, and these are all static (and
non-static) members of the string class (since they operate on strings,
that's where Victor things they should be). Now think about the job
involved in changing the string class to use reference counting, or SSO
- which of those utility methods needs changing? Some? All? You have to
check each of them in any case - this could mean checking and possibly
reimplementing 50 utility functions. Compare to the alternative, where
the string interface is minimal and complete, and the utility functions
are separate, namespace scope, non-friend functions. None of them needs
to be modified for the change of implementation.

Tom
 
F

Fei Liu

Tom said:
Yes, ADL allows unqualified names from other namespaces to be considered
in function calls. But you can qualify function calls to prevent this.
Quite a common practice is to use quite small namespaces (in contrast to
the monolithic namespace std), that only contain a class and its related
functions.

Here is an example of ADL pulling in unqualified names from namespaces
so namespace scope is quite vulnerable if not coded carefully,

namespace n1{
struct S {};
void foo(S) {}
}


void bar(){
n1::S s;
foo(s);
}
 
D

Daniel T.

Tom Widmer said:
Daniel said:
The scope of a member function
is the class of which it's the member. That's smaller than the enclosing
[namespace or other type] scope. I thought that was obvious.


I thought the "scope" of a variable or function had something to do with
where it can be used in the program. Am I wrong?
Yes.


In the following code:

class Foo {
public:
static int bar();
};

int bar();

does "Foo::bar()" have a smaller scope than "bar()" or not? If it does,
what does "scope" mean?

The scope is the declarative region of the program in which the name (in
unqualified form, importantly) is valid.

Thank you for the explanation. Previously, I was of the opinion that
visibility defined scope, and the only thing that making 'func()' a
public static member (or a member of a namespace for that matter) got
you was a mangled name so it was less likely to be called in incorrect
contexts.

Hopefully, I will not confuse scope and visibility again. :)
 
F

Fei Liu

Daniel said:
Tom Widmer said:
Daniel said:
The scope of a member function
is the class of which it's the member. That's smaller than the enclosing
[namespace or other type] scope. I thought that was obvious.


I thought the "scope" of a variable or function had something to do with
where it can be used in the program. Am I wrong?
Yes.


In the following code:

class Foo {
public:
static int bar();
};

int bar();

does "Foo::bar()" have a smaller scope than "bar()" or not? If it does,
what does "scope" mean?

The scope is the declarative region of the program in which the name (in
unqualified form, importantly) is valid.

Thank you for the explanation. Previously, I was of the opinion that
visibility defined scope, and the only thing that making 'func()' a
public static member (or a member of a namespace for that matter) got
you was a mangled name so it was less likely to be called in incorrect
contexts.

Hopefully, I will not confuse scope and visibility again. :)

If I understand correctly, names in one scope may or may not be visible
in another scope. The rules that define visibility crosses but not
coincide with rules that define scoping.
 
S

SuperKoko

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.

Having a small, sensible, significant set of member functions. Other
functions should be non-member.
I like to think that the public interface, as defined in the class
definition, is the "mathematical definition of the object class".

Member functions have access to internal details of the class.
The encapsulation is here, in order to have the smallest number of
functions accessing a particular implementation detail.

I think that these two Herb Sutter's articles are revelant here:
http://www.gotw.ca/publications/mill02.htm
http://www.gotw.ca/gotw/084.htm
 

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,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top