Lesson 8 - Constant methods in C++
In the previous lesson, Arena with warriors in C++, we finished our object-oriented arena. In today's tutorial, we're going to find out what constant methods are and when to use them.
Constant methods
As the name suggests, constant method is a method that does not change
instance data. This applies for all getters - they only get data, they don't
change anything. Therefore, all getters should be marked as constant. The
Warrior.alive()
method is also a kind of getter because it only
determines whether the warrior has enough health. All methods that don't change
data should be marked as constant. We do this simply by adding the
const
keyword after the method name (to both the .h
and .cpp
files). For example, it'd look like this for our
Warrior
class:
Warrior.h
#ifndef __WARRIOR_H_ #define __WARRIOR_H_ #include <string> #include "RollingDie.h" using namespace std; class Warrior { private: float health; float max_health; float damage; float defense; RollingDie ¨ public: Warrior(float health, float damage, float defense, RollingDie &die); bool alive() const; float attack(Warrior &second) const; float getHealth() const; float getMaxHealth() const; float getDamage() const; float getDefense() const; }; #endif
Warrior.cpp
#include "Warrior.h" Warrior::Warrior(float health, float damage, float defense, RollingDie &die) : die(die), health(health), max_health(health), damage(damage), defense(defense) {} bool Warrior::alive() const { return this->health > 0; } float Warrior::attack(Warrior & second) const { float defense_second = second.defense + second.die.roll(); float damage_first = this->damage + this->die.roll(); float injury = damage_first - defense_second; if (injury < 0) injury = 0; second.health -= injury; return injury; } float Warrior::getHealth() const { return this->health; } float Warrior::getMaxHealth() const { return this->max_health; } float Warrior::getDamage() const { return this->damage; } float Warrior::getDefense() const { return this->defense; }
Note that even the attack()
method is constant - it does not
change instance data, it just changes parameter data.
Rules
Why are we doing this? The const
keyword will make sure that the
method is properly implemented. If we defined the setSidesCount()
method for the RollingDie
class and set it as constant, the
compiler would report us the following message (for Visual Studio):
error C2228: left of '.sides_count' must have class/struct/union note: type is 'const RollingDie *const ' note: did you intend to use '->' instead?
The compiler will ensure that the function cannot change anything, and inform other programmers that the data is safe (it can't be changed).
What other rules apply? We can only call constant methods from a constant
method. This means that when we set a getter as constant, we can't call a setter
from it (otherwise the keyword const
wouldn't make much sense since
the method would modify the data).
There's also a third and most important rule: only constant methods can be called on constant objects. For example, for our warrior. Let's assume that the following code should work:
const Warrior warrior(100, 8, 5, die); float damage = warrior.getDamage(); float defense = warrior.getDefense(); float aggressivity_level = damage - defense;
Why would we assume that? Although the warrior is constant, we want to get the damage and defense only to read them - this shouldn't be a problem because the methods don't change the data and the constant isn't broken. But it's us who knows that, but the compiler doesn't know it yet. In order for this code to work, we must set the getters as constant - the compiler will then be certain that the method won't change anything and can be called on a constant object.
Pointers
For pointers and references, the situation becomes a little more complicated.
By adding the const
keyword to the method name, we can imagine that
it marks all the fields as constant. For demonstration, let's consider the
following class:
class User { int age; char* name; void printName() const; };
Inside the printName()
method, the const
will make
the this
pointer of the User const * const
type (as
opposed to User * const
). Just to make sure, we always read
pointers backwards, so User const * const
is a "constant pointer to
a constant user", while User * const
is a "constant pointer to a
user". We also have two syntax options, so the following two samples are
equivalent: User const *
and const User *
.
So we can imagine a constant method as all the fields are constant:
class User { const int age; char * const name; };
Note the type of the name
field. It's a constant pointer (we
can't change the address where it points to), but it's not a pointer to a
constant value - we are still able to change the name. Constant methods do not
guarantee that instance data will not change - it only ensures that fields do
not change.
Problems occur when working with references as well. Let's suppose we want to
return the user's age by reference. It's not usual, but for some reason we want
it. We can't use a reference to int
because the field type is
const int
and the types must match. This means that we must use a
constant reference:
class User { int age; char* name; void printName() const; const int& getAge() const; };
Now this code part would work, and when we think about it, it literally didn't work until now - it respects constant values where it makes sense.
That would be all for today's shorter lesson. In the source code, constants were added to the getters and to a few other methods (for which it made sense). Next time, we'll continue with the code, so I recommend downloading the source code below the article. You will be sure that we'll work with the same source code. Next time, in the Static Class Members in C++ lesson, we have static members waiting for us.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 6x (1.08 MB)
Application includes source codes in language C++