Get up to 80 % extra points for free! More info:

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 &die;
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++

 

Previous article
Arena with warriors in C++
All articles in this section
Object-Oriented Programming in C++
Skip article
(not recommended)
Static Class Members in C++
Article has been written for you by David Capka Hartinger
Avatar
User rating:
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities