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.
--
Ian Collins
Here's a simple example I just wrote that performs a clearly useful
encapsulation of the Fibonacci sequence. Any student can understand
the sequence, and how hiding the details simplifies the client code in
main(). The constructor performs an important function, and it shows
the use of const versus non-const. I felt that using operator
overloads (++ and operator ()) weren't appropriate. I took some
liberty with whitespace, showing how when something is short and
trivial, one might use fewer newlines.
#include <iostream>
class Fibonacci {
int a, b;
public:
Fibonacci();
int value() const { return a; }
void next();
};
Fibonacci::Fibonacci() : a( 0 ), b( 1 ) { }
void Fibonacci::next()
{
int fib = a + b;
a = b;
b = fib;
}
int main()
{
Fibonacci f;
for ( int n = 10; n--; )
{
std::cout << f.value() << ',';
f.next();
}
std::cout << '\n';
return 0;
}
In that case, call it something like calculateBalance.
--
Ian Collins
--
Ian Collins
A procedure primarily does something whose side-effects persist after it
returns. A function primarily gives the caller the value of something, and
doesn't usually have any side-effects beyond determining the return value.
> 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.
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
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
> > 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?
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.)
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();
};
>
> --
> Ian Collins
Bart v Ingen Schenau
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.
> 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
--
Quidquid latine scriptum est, altum videtur.
> 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
specific includes of <ios>, <streambuf>, <istream> and
<ostream>.
> >>int main()
> >>{ Account acct( 100 );
> >> ::std::cout << acct.::Account::balance() << '\n';
> >> ::std::cout << acct.balance() << '\n';
> >>}
> >Why both?
> 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.
> >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.
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.
> 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.)