An kind of member function name scope specification

S

Stefan Ram

For teaching purposes, I wrote this program as a first
example of a definition of a class with non-static functions:

#include <iostream>
#include <ostream>

class Account
{ private:
double balance_;
public:
Account( double const balance );
double balance(); };

::Account::Account( double const balance )
{ this->balance_ = balance; }

double ::Account::balance()
{ return this->balance_; }

int main()
{ Account acct( 100 );
::std::cout << acct.::Account::balance() << '\n';
::std::cout << acct.balance() << '\n'; }

(End of program.)

Regarding »acct.::Account::balance()«:

I was not aware that such a full qualification of a member
function in a member function call was possible at all.

When would one need this? (To call an overloaded function
of a base class?)

Can the above definition of the class still be improved somehow?

~~

Is such an account class is a good example for teaching?

I need the first example to be very simple, yet it needs
to suggest to be useful and related to applications of
the language.

Are there any other good examples of classes, that are

- small and simple
- not already part of the standard library
- not requiring special knowledge of a field
- looking somewhat natural and useful

?

Account classes are used often.

Sometimes, a class for complex numbers is used in teaching,
but such a class already is defined in the standard library
and might look too »mathematical«.

The only other topic that comes to my mind right now
would be a class for a compound date (YYYY, MM, DD)
or time (HH, MM) object.
 
S

Stefan Ram

::Account::Account( double const balance )
Can the above definition of the class still be improved somehow?

Except for using a member initialization list,
which I intentionally omitted in this first example.
 
I

Ian Collins

Stefan said:
For teaching purposes, I wrote this program as a first
example of a definition of a class with non-static functions:

#include <iostream>
#include <ostream>

class Account
{ private:
double balance_;
public:
Account( double const balance );
double balance(); };

::Account::Account( double const balance )
{ this->balance_ = balance; }

double ::Account::balance()
{ return this->balance_; }

int main()
{ Account acct( 100 );
::std::cout << acct.::Account::balance() << '\n';
::std::cout << acct.balance() << '\n'; }

(End of program.)

Regarding »acct.::Account::balance()«:

I was not aware that such a full qualification of a member
function in a member function call was possible at all.

When would one need this? (To call an overloaded function
of a base class?)

Yes, try adding

struct Base
{
double balance() { return 42; }
};

class Account : public Base

and

std::cout << acct.::Base::balance() << '\n';
Can the above definition of the class still be improved somehow?

You can do away with the superfluous leading :: :):std::).

I assume the layout was to compress the code for Usenet?

The this-> in balance() is superfluous, as is the leading ::.

balance() should be declared cost if it doesn't change anything.

No too big a deal here, but in general geters are a design smell.
 
S

Stefan Ram

Ian Collins said:
I assume the layout was to compress the code for Usenet?

The above layout contains 4 blank lines,
separating the 5 major parts of the source:

- include directives
- a class specifier
- a member function definition
- another member function definition and
- a function definition

If I would add additional empty lines within
each of these parts, those 4 top-most separators
would not stand out in the prominent way the do now.

So this is some kind of global scope view.

If I would show only the class specifier, I might
have used vertical spacing for this, maybe like:

class Account
{
private:
double balance_;

public:
Account( double const balance );
double balance(); };

So, maybe I do not consider formatting to be static, but to be
dynamic. The format used by me in the OP was focused on the
large-scale structure of the source code. If I would have to
edit a function definition, I might start by »unpacking« it,
that is, by inserting some vertical space to see more of the
smaller-scale structure. I assume that readers will adapt the
layout to their current need. I do not deem the layout chosen
by me to be best for all purposes.
 
S

Stefan Ram

Ian Collins said:
No too big a deal here, but in general geters are a design smell.

Yes, but I do not see a getter here. Let me explain why:

I have quoted the public interface of the class above.

It misses an important feature: documentation.

So I will add that document now in the way the names suggest:

- The constructor initializes a new account with
an opening balance as the argument.

- The function »balance« returns the current balance
of the account.

No mentioning of private fields in the documentation.

So a client/caller/reader of this interface can not know
whether this function returns the value of a private field or
obtains the balance using some other means. He does not know
which fields an object of this class has. Therefore, I do not
deem »balance« to be a getter, just a non-void function.

I would see a getter, if the documentation would read instead:

- Each object of this class has a field named »balance_«.
The function »balance« returns the value of this field.

(Admittedly, my code does not have documentation, so both
interpretations are possible. But the first one was intended.)
 
I

Ian Collins

Stefan said:
Yes, but I do not see a getter here. Let me explain why:

I have quoted the public interface of the class above.

It misses an important feature: documentation.

So I will add that document now in the way the names suggest:

- The constructor initializes a new account with
an opening balance as the argument.

- The function »balance« returns the current balance
of the account.

No mentioning of private fields in the documentation.

So a client/caller/reader of this interface can not know
whether this function returns the value of a private field or
obtains the balance using some other means. He does not know
which fields an object of this class has. Therefore, I do not
deem »balance« to be a getter, just a non-void function.

In that case, call it something like calculateBalance.
 
S

Stefan Ram

Ian Collins said:
In that case, call it something like calculateBalance.

I deem that to be ugly. I still follow the advice Rob Pike
gave 20 years ago:

»Procedure names should reflect what they do;
function names should reflect what they return.«

Rob Pike; »Notes on Programming in C«; February 21, 1989

http://www.lysator.liu.se/c/pikestyle.html

Also, this is consistent with ISO/IEC 14882:2003(E), where in
<cmath>, the function is named »::std::sin«, not »::std::calculateSin«.

Also, either »calculate« has no meaning at all, or
»calculateBalance« is /more/ transparent than »balance«,
because it leaks the implementation detail that the result of
this call is »calculated« - while »balance« alone is
completely opaque, yielding no information whatsoever about
the implementation, just stating what is returned.
And exposing implementation details is exactly why getters
are being criticized.
 
I

Ian Collins

Stefan said:
I deem that to be ugly. I still follow the advice Rob Pike
gave 20 years ago:

»Procedure names should reflect what they do;
function names should reflect what they return.«
In the context of C++, what is a function and what is a procedure?
 
S

Stefan Ram

Ian Collins said:
In the context of C++, what is a function and what is a procedure?

Rob Pike wrote this in the context of C, where it might be
asked as well.

I assume that the terms stem from the Pascal culture.

A Pascal procedure corresponds to a function not returning a
value in C or C++.

A Pascal function corresponds to a function returning a value
in C or C++.

There are functions, like »fopen« that return a value, but are
still named for what they do. This expresses that the main
purpose is deemed to be the action and the value is a
secondary purpose (serving the main purpose by giving a
feedback about the success of the attempted action).

There are functions, like »rand« that have an effect, but
still are named for what they return, because their result is
deemed to be their main purpose, while their effect is deemed
a secondary purpose. (The effect of »rand« is to advance the
random number generator to the next state.)

Naming is especially easy, when a function either only has an
effect or only has a value. In the case of »fopen«, this
separation can be done using OOP as follows:

{ FileOpenAttempt fileOpenAttempt( "source.txt", "r" );
fileOpenAttempt.run();
if( fileOpenAttempt.suceeded() )
{ FILE * file = fileOpenAttempt.result();
use( file );
file.close(); }
else
{ ::std::cerr << fileOpenAttempt.report() << '\n'; }}

Here, »run« is a pure action, while the other element
functions only return a value, but do no action. This is akin
to the structure of natural language, where phrases either
express actions (verbal phrases) or things (noun phrases), but
never both. Insofar, »if( fopen( ... ))« is somewhat
unnatural, but one can get used to it.
 
J

James Kanze

Yes, but I do not see a getter here. Let me explain why:
I have quoted the public interface of the class above.
It misses an important feature: documentation.
So I will add that document now in the way the names
suggest:
- The constructor initializes a new account with
an opening balance as the argument.
- The function »balance« returns the current balance
of the account.
No mentioning of private fields in the documentation.
So a client/caller/reader of this interface can not know
whether this function returns the value of a private field
or obtains the balance using some other means. He does not
know which fields an object of this class has. Therefore, I
do not deem »balance« to be a getter, just a non-void
function.

I wonder if both you and Ian aren't confusing design level and
implementation level a bit. From a design level, an Account
certainly "hasA" balance (or currentBalance, or whatever), and
that balance is a value, not a behavior. At the design level,
some classes really are just glorified data containers, some are
pure behavior, and some are a mixture of the two (although I
find the latter occur fairly rarely). At the design level, I
can't find anything to say against a class Account which
contains a function balance() (or getBalance(), depending on the
coding guidelines---but I prefer balance(), because it is
logically an attribute).

At the implementation level, of course, whether the class has a
private member, which the function simply returns, or does
something else, is irrelevant. (We use the function, rather
than a public data member, precisely to keep it irrelevant.)
I would see a getter, if the documentation would read
instead:
- Each object of this class has a field named »balance_«.
The function »balance« returns the value of this field.

Which is, precisely, an example of confounding an implementation
detail and a design aspect (in this case, the contract). The
class has an attribute balance. It may be implemented by a
single private variable, or not. But that's none of the client
code's business.
 
B

Bart van Ingen Schenau

  For teaching purposes, I wrote this program as a first
  example of a definition of a class with non-static functions:
  (End of program.)

It is good you inserted this comment, because I nearly overlooked the
closing brace of main.
I hope you will give the closing braces more prominence in the code
you present to the students, especially if the opening brace is not on
the same line.
  Regarding »acct.::Account::balance()«:

  I was not aware that such a full qualification of a member
  function in a member function call was possible at all.

  When would one need this? (To call an overloaded function
  of a base class?)

You would only need it to disable the virtual-call mechanism. Calling
the base-class implementation of a virtual function is the most common
use.
  Can the above definition of the class still be improved somehow?

I would add two more member functions, to make the class more useful:
void Account::deposit(double);
void Account::withdraw(double);

Otherwise, students will wonder what the use of an account is if any
change to the balance requires you to create a new account.
                            ~~

  Is such an account class is a good example for teaching?

Yes. It has a clear link to a real-world concept that the students
will be familiar with and you can later revisit the Account class when
dealing with more advances topics, like interaction between object.

Bart v Ingen Schenau
 
J

James Kanze

In the context of C++, what is a function and what is a
procedure?

Should names be chosen "in the context of C++", or according to
the design. In his case, "balance" is an attribute of the
class; "balance()" is a function which returns the current
value of that attribute. Regardless of how it is determined.
(In such simple cases, of course, it's hard to imagine it being
anything but a member variable, but in other cases... A complex
number has four attributes, real part, imaginary part, modulus
and argument, but I can't imagine an implementation actually
storing all four as member variables.)
 
B

Bart van Ingen Schenau

No too big a deal here, but in general geters are a design smell.

I agree that getters are a design smell, but I did not see anything I
would classify as a getter in Stefan's class.

Regardless how you implement an Account class, one intrinsic property
of an account is that it has a balance.
Would you classify the function balance() in the following class also
as a getter (and why)?

class Account {
private:
double initialDeposit;
std::vector<double> mutations;
public:
Account(double deposit) : initialDeposit(deposit) {}
double balance()
{ return std::accumulate(mutations.begin(), mutations.end(),
initialDeposit); }
void deposit();
void withdraw();
};

Bart v Ingen Schenau
 
J

James Kanze

For teaching purposes, I wrote this program as a first
example of a definition of a class with non-static
functions:

Several comments.
#include <iostream>
#include <ostream>

The inclusion of <ostream> isn't necessary. It never was in
practice, and the standard will require that it not be in the
future. (I don't think this is really relevant to the pedagogic
goals of the program, but IMHO, the less "unexplained magic" you
throw at the students, the better.)
class Account
{ private:
double balance_;
public:
Account( double const balance );
double balance();
};

Several points:

Formatting:
I'd definitely put "private:" and "public:" on lines
by themselves, and indented less than the rest---they're not
lines like the others---they control what follows.

Constructor:
I'd declare it "explicit". I also wouldn't use the const in
the parameter declaration---it's just comment, as far as the
compiler is concerned, and has no relevance to the client
code.

Accessor:
I'd definitely make balance() const.
::Account::Account( double const balance )
{ this->balance_ = balance; }

There's no need for the this-> if the names are different.

If you really want to encourage good practice, the member
variable should be named balance, and the parameter
initialBalance. Lot of people, including myself, don't always
bother. Which makes some convention for differentiating the
names important---I like myBalance and balance, but it's really
a matter of style. (Except that a trailing underscore isn't
very visible, and should probably be avoided.) But as a
learning exercise, I think I'd stick with balance and
initialBalance, rather than risk having to explain why I need a
convention to differentiate the names.
double ::Account::balance()
{ return this->balance_; }

No need for the :: before Account, nor for the this->.
Idiomatic C++ wouldn't use either.
int main()
{ Account acct( 100 );
::std::cout << acct.::Account::balance() << '\n';
::std::cout << acct.balance() << '\n';
}

Why both?
(End of program.)
Regarding »acct.::Account::balance()«:
I was not aware that such a full qualification of a member
function in a member function call was possible at all.
When would one need this? (To call an overloaded function of
a base class?)

Using a qualified id has different semantics if the function is
virtual; it suppresses dynamic lookup. It also ensures that
lookup starts at the given class, and not in some derived class,
e.g.:

class DerivedAccount : public Account
{
public:
double balance() { return 2.0 * Account::balance() ; }
explicit DerivedAccount( double initialBalance )
: Account( initialBalance )
{
}
} ;

DerivedAccount account( 42.0 ) ;
std::cout << account.balance() << std::endl ; // displays 84
std::cout << account.Account::balance() << std::endl ;
// displays 42

It's something I wouldn't present until much later, once the
students have mastered inheritance.
Can the above definition of the class still be improved
somehow?

See above. The main criticism I have is that there are parts
that aren't at all idiomatic---using this->, for example, when
not necessary, or making a parameter const (or a function which
returns an attribute non-const).
Is such an account class is a good example for teaching?

Probably. It lends itself to extension very well, e.g.
functions like credit and debit. (Which in turn show why an
attribute of a class is not a global variable.)
I need the first example to be very simple, yet it needs
to suggest to be useful and related to applications of
the language.
Are there any other good examples of classes, that are

- small and simple
- not already part of the standard library
- not requiring special knowledge of a field
- looking somewhat natural and useful

Account classes are used often.

They also have the advantage of allowing an early introduction
to the distinction of entity classes (Account) vs. values (the
balance, etc.).
Sometimes, a class for complex numbers is used in teaching,
but such a class already is defined in the standard library
and might look too »mathematical«.

On the other hand, it's a classical example of a class which has
attributes which aren't directly represented by a member
variable.
The only other topic that comes to my mind right now would
be a class for a compound date (YYYY, MM, DD) or time (HH,
MM) object.

That will become very complicated, very quickly.
 
S

Stefan Ram

James Kanze said:
The inclusion of <ostream> isn't necessary. It never was in
practice, and the standard will require that it not be in the
future. (I don't think this is really relevant to the pedagogic
goals of the program, but IMHO, the less "unexplained magic" you
throw at the students, the better.)

(I would like to thank everyone for their responses, including
authors of posts I have not directly replied to.)

I remember this topic (<ostream>) from several other Usenet
discussions, but I do not yet had knowledge that the
forthcoming new C++ standard will clarify this. If someone
knows about the specific locations where in the upcoming
standard this was changed, so that I can look it up, I would
be grateful for any hint.
Why both?

(Thanks for your comments, including those I have not quoted.)

My usual approach in teaching is to show the most general
maximally qualified notation first and then show any possible
or common abbreviations. So above I wanted to show that both
statements have the same behavior, here.
See above. The main criticism I have is that there are parts
that aren't at all idiomatic---using this->, for example,

This follows the same idea: I start using the most explicit
usage first (»this->balance_«), then I will explain that
usually »this->« can be omitted and henceforth I will omit it
indeed whenever possible.
 
M

Martin Eisenberg

James said:
I like myBalance and balance, but it's really a matter of style.
(Except that a trailing underscore isn't very visible, and should
probably be avoided.)

Isn't leanness the point of the trailing-underscore convention? The
knowledge of what's "mine" is not usually that hard to track once you
have some idea of how a method operates (which is a prerequisite for
doing anything to it); at least in the code I mostly read, almost all
manipulated non-locals are members. So arguably the information
should be there but not in yer face.

Personally, I dislike any kind of prefix convention because it
obscures the beginning of tokens, the most salient visual cue for
word recognition. The obtrusion is grantedly slight but constant --
though perhaps, being the kind that typos impress themselves on
before subheadings, I'm more susceptible to the effect. I suppose the
"m_" prefix minimizes this but a suffix is still more pleasant.


Martin
 
J

James Kanze

I remember this topic (<ostream>) from several other Usenet
discussions, but I do not yet had knowledge that the
forthcoming new C++ standard will clarify this. If someone
knows about the specific locations where in the upcoming
standard this was changed, so that I can look it up, I would
be grateful for any hint.

In the same place it didn't say it before:). At the top of
§27.3, there is a synopsis of the contents of the <iostream>
header. In the orginal standard, only the eight standard
objects are mentionned; in the current draft, there are also
[QUOTE= said:
Why both?
[/QUOTE]
My usual approach in teaching is to show the most general
maximally qualified notation first and then show any possible
or common abbreviations. So above I wanted to show that both
statements have the same behavior, here.

Except that they don't, at least in general. In C++, a
qualified id is used in this context to "turn off" virtualness,
since there's no virtualness involved in your example, the
qualification has no effect, but it certainly isn't the most
general notation.
This follows the same idea: I start using the most explicit
usage first (»this->balance_«), then I will explain that
usually »this->« can be omitted and henceforth I will omit it
indeed whenever possible.

Hmmm. I'm not sure I agree. I'd start by saying that a name
represents a class member (will be looked up as a class member
first). The possibility of using this-> is simply a means of
disambiguating the name, e.g. if it is masked by a local
variable or parameter, or making it dependent in a template (but
you don't think you want to get into that right away). I'm not
sure I'd even present the notation until the need actually
arised, but if I did, I'd certainly present the more usual
notation simultaneously.
 
J

James Kanze

Isn't leanness the point of the trailing-underscore convention? The
knowledge of what's "mine" is not usually that hard to track once you
have some idea of how a method operates (which is a prerequisite for
doing anything to it); at least in the code I mostly read, almost all
manipulated non-locals are members. So arguably the information
should be there but not in yer face.

The information is superfluous. The only justification for it
is that you have a poor naming convention, and end up with
parameters and member variables that would otherwise have the
same name. The use of a convention here is to create two
different, distinctive names, not to encode some
meta-information along the lines of Hungarian notation. And two
different, distinctive names should be visually different; when
you see the name used, you want to be able to immediately
recognize which object is being referred to.
Personally, I dislike any kind of prefix convention because it
obscures the beginning of tokens, the most salient visual cue
for word recognition. The obtrusion is grantedly slight but
constant -- though perhaps, being the kind that typos impress
themselves on before subheadings, I'm more susceptible to the
effect. I suppose the "m_" prefix minimizes this but a suffix
is still more pleasant.

The use of a prefix stems from the fact that in English,
qualifiers preceded the qualified. Thus, we say "my status" and
"initial status", and not "status my" and "status initial". So
the member variable becomes "myStatus" and the argument to the
constructor "initialStatus". (Variable names are qualified
nouns, remember.)
 

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

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top