C++’s mutable and conceptual constness (cited)

article origin: http://www.highprogrammer.com/alan/rants/mutable.html

Mutable

The keyword mutable is used to allow a particular data member of const object to be modified. This is particularly useful if most of the members should be constant but a few need to be updateable. Suppose we add a “salary” member to our Employee class. While the employee name and id may be constant, salary should not be. Here is our updated class.

class Employee {
public:
    Employee(string name = "No Name", 
        string id = "000-00-0000",
        double salary = 0)
    : _name(name), _id(id)
    {
        _salary = salary;
    }
    string getName() const {return _name;}
    void setName(string name) {_name = name;}
    string getid() const {return _id;}
    void setid(string id) {_id = id;}
    double getSalary() const {return _salary;}
    void setSalary(double salary) {_salary = salary;}
    void promote(double salary) const {_salary = salary;}
private:
    string _name;
    string _id;
    mutable double _salary;
};

Now, even for a const Employee object, the salary may be modified.

const Employee john("JOHN","007",5000.0);
....
....
john.promote(20000.0);

No, no! A thousand times, no!

I’ve seen this sort of terrible idea before. This sort of madness leads to flawed code and defeats the entire purpose of const in C++. I can only conclude that the people writing this sort of nonsense themselves don’t understand the purpose of mutable. So they teach a mistake, passing on this nonsense to the next group of C++ programmers who pass it on themselves. This must stop.

When you mark a variable const, you are promising (and asking C++ to enforce) that you will never logically modify the contents of that object. Perhaps the most useful reason to do this is when you pass an object into a function by reference or pointer. By making it const, the function promises to not mess with your object. For example, say you have a class Robot that inherits from Person. You want to pass your Robot into the function take_pulse. You want take_pulse to use Robot’s overridden methods, so take_pulse takes the object by reference. Because it’s const, you can be confident that take_pulse won’t modify the Robot, just read from it:

class Person {
public:
    virtual bool has_pulse() const { return true; } 
    void set_name() { /* ... */ }
};

class Robot : public Person {
public:
    virtual bool has_pulse() const { return false; }
    void set_name() { /* ... */ }
};

/*
Because Person is const, take_pulse cannot call set_name().
Because Person is a reference, we can pass in a Robot robot
and get the correct answer (false).
*/
bool take_pulse( const Person & X ) {
    return X.has_pulse();
}

It’s nonsense to make the salary mutable; you’re just making it possible for code that gets a constant object to mess with the salary. If the employee is constant, you shouldn’t be messing with his salary.

So what if you want the employee’s name and id to be constant, but not the salary? Well, just say so!

class Employee {
public:
    Employee(string name = "No Name",
        string id = "000-00-0000",
        double salary = 0)
    : _name(name), _id(id)
    {
        _salary = salary;
    }
    string getName() const {return _name;}
    string getid() const {return _id;}
    double getSalary() const {return _salary;}
    void setSalary(double salary) {_salary = salary;}
    void promote(double salary) {_salary = salary;}
private:
    const string _name;
    const string _id;
    double _salary;
};

Now they’re constant. Of course, this means you can only set them in the constructor.

So if the above madness isn’t what mutable is for, what is it for? Here’s the subtle case: mutable is for the case where an object is logically constant, but in practice needs to change. These cases are few and far between, but they exist.

Here’s one example: You have a constant object, but for debugging purposes want to track how often a constant method is called on it. Logically you’re not changing the object. Note that if you’re making decisions in your program based on a mutable variable, you’ve almost certainly violated logical constness and need to rethink things.

class Employee {
public:
    Employee(const std::string & name) 
        : _name(name), _access_count(0) { }
    void set_name(const std::string & name) {
        _name = name;
    }
    std::string get_name() const {
        _access_count++;
        return _name;
    }
    int get_access_count() const { return _access_count; }private:
    std::string _name;
    mutable int _access_count;
};

As a more complex example, you might want to cache the results of an expensive operation:

class MathObject {
public:
    MathObject() : pi_cached(false) { }
    double pi() const {
        if( ! pi_cached ) {
            /* This is an insanely slow way to calculate pi. */
            pi = 4;
            for(long step = 3; step < 1000000000; step += 4) {
                pi += ((-4.0/(double)step) + (4.0/((double)step+2)));
            }
            pi_cached = true;
        }
        return pi;
    }
private:
    mutable bool pi_cached;
    mutable double pi;
};

Now we don’t calculate pi until someone asks for it, but when they do we cache the result, which is good because we’re calculating it in a really slow and stupid way. Logically the function is still const (pi isn’t about to change).

Ultimately you almost certainly do not need mutable at any given moment. I’ve gone years between wanting the mutable keyword. If you think you need mutable, think twice. Be sure that the object will still be logically constant, even as its internals change.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s