Ian said:
That would happen as if by magic once you changed it from a struct to
a class!
If you allow me to butt in...
Having accessors is not yet the end of the world. However, the OP should
think first and foremost about the interface to the class, and not about
the data that are stored in.
Joe,
For some reason you decided to begin with the class implementation. That
is not right. I think I know why it's so. You might think: "OK, I have
a player. What properties does a player have? It has a name, its age,
and some other stuff. Let's give it those data members, and proceed from
there." It started OK, it only progressed in the wrong direction. Let's
begin again. You have a "player". In your model domain a "player" has
a "name". What for? So it can be identified by it, right? So, the class
'Player' needs a way so that somebody can query the "name" of an instance.
And we can safely establish that whoever asks for the "name" expects no
less (and no more) than a 'std::string' object.
So we write
class Player {
public:
std::string name() const; // I made it const because it's unlikely
// to change anything in the object
};
That's our first step in the design process. What's next? How does each
'Player' instance get its "name"? How did you get your name? Your parents
(most likely) named you when you were born. So, it makes sense to give
the class 'Player' a constructor with an argument, which will designate
the instance being constructed with a name:
class Player {
public:
Player(std::string name);
std::string name() const;
};
OK, good. As some would suggest, you need to make a test case immediately
for that functionality to provide the test platform and an example of how
to use the class:
int main() {
Player player("Joe");
assert(player.name() == "Joe");
}
If you combine the two pieces, the program won't compile yet, but it will
be very close. What do you need now? You need to _implement_ those two
functions (the constructor and the 'name' member). Only *now* do you get
to pick _how_ the "name" of a 'Player' instance gets stored. For all we
know, you could actually have a global table of names and each player
would store an index in that table...
But let's not overcomplicate things. Let 'Player' have a data member,
so it can store its own name and give it when asked:
class Player {
std::string n;
public:
Player(std::string name);
std::string name() const;
};
If that's our initial layout of the 'Player' class, let's now create the
bodies of the member functions:
Player:
layer(std::string name) : n(name) {
// nothing here
}
std::string Player::name() const {
return n;
}
Why did we call the data member 'n'? No reason. Call it what you will.
It's only important to _you_, not to any user of that class.
Now, let's just evaluate a couple of things here. Is a 'Player' allowed
to change its name? Generally, yes. In real life, anybody can change
their name. The only limitation is (a) only after 18 years of age and
(b) by applying to a judge, and don't forget to give good enough a reason
for the name change.
So, why not limit [y]our class in that regard and prohibit it from ever
changing the name of an instance? I say, let's. How? We can make the
'n' member constant and not create any functionality to change the value
of that member during the lifetime of the object:
class Player {
const std::string n;
public:
Player(std::string name) : n(name) {}
std::string name() const { return n; }
};
What else can we improve here? Not much, but I'd do two things. First
of all, let's (for now, until we have a good reason not to) prohibit
implicit conversion from 'std::string' to 'Player' by declaring it one-
argument constructor 'explicit', and let's for the sake of keeping good
habits, pass the argument to the constructor by a reference to const:
class Player {
const std::string n;
public:
explicit Player(std::string const & name) : n(name) {}
std::string name() const { return n; }
};
That's it. We're done so far with the name. Now we can proceed to
the 'age'.
What's important about the age? If a 'Player' were a model of a real-
life person, at creation its age would be 0. And then, as 'time' would
pass in the simulation ("program"), each player would age accordingly.
Well, when we're born, we're rarely players, so we cannot model [y]our
'Player's as if they were real people. However, we could follow the
same logic as with the name. For the duration of the game, the 'age'
is unlikely to change. So, it is probably logically correct to prohibit
any changes to 'age' during the lifetime of a 'Player' object. And when
does it get its age? We should provide the mechanism to give every
'Player' some 'age', and the most logical time is during *construction*
(again)...
I'll leave this exercise to you. As soon as your 'Player' has two (and
not one) arguments in its constructor, you don't need "explicit" any
longer, but it shouldn't hurt. Don't forget to write a proper test for
'age' as well.
Good luck!
V