liveBook · Manning

Immutable instance variable

As explained in 100 C++ Mistakes and How to Avoid Them

An immutable instance variable is a data member whose value cannot be changed after its initialization.

An immutable instance variable keeps its initialized value unchanged. Remove its mutator: unnecessary setters permit invalid state and increase maintenance costs. section 8.3

Apply the pattern while maintaining the class invariant:

  • Make as many instance variables immutable as practical. section 8.3
  • Eliminate mutators for immutable variables instead of exposing trivial assignment methods. section 8.3
  • Validate constructor inputs and mutator inputs through one shared validation method when mutation remains necessary. section 8.3

Circle radius and the trivial setter

A Circle with setRadius can accept -1, violating the intended radius rule. If radius must remain fixed after construction, remove setRadius; if it must change, validate every update and keep validation knowledge in one place. section 8.3

Listing 8.3 A class with a trivial nonvalidating mutator
class Circle {
private:
    double radius;
public:
    Circle() : radius(1) {}
    Circle(double radius) : radius(radius) {}
    double getRadius() { return radius; }
    void setRadius(double radius) { this->radius = radius; }    #1
};
 
int main() {
    Circle c1(2);
    std::cout << c1.getRadius() << '\n';
    c1.setRadius(-1);
    std::cout << c1.getRadius() << '\n';
}

Inspect the problematic Circle implementation, where a trivial mutator assigns an unchecked negative radius. section 8.3

Listing 8.5 A class with validation and a single source of knowledge
class Circle {
private:
    double radius;
    static double validateRadius(double radius) {              #1
        if (radius < 0)
            throw std::invalid_argument("radius is negative");
        return radius;
    }
public:
    Circle() : radius(1) {}
    Circle(double radius) : radius(validateRadius(
        radius)) {}                                            #2
    double getRadius() const { return radius; }
    void setRadius(double r) { radius = 
        validateRadius(r); }                                   #3
};
 
int main() {
    Circle c1(2);
    std::cout << c1.getRadius() << '\n';
    Circle c2(-1);                                             #4
    std::cout << c2.getRadius() << '\n';
}

Compare the corrected implementation, which centralizes radius validation for construction and permitted mutation. section 8.3

How 100 C++ Mistakes and How to Avoid Them covers it

How deep it goes
Defines immutability through absent mutators and connects it to validation and duplicated knowledge.
Examples
Uses a Circle radius, a negative-radius mutation, and centralized constructor and mutator validation.
What you can do after
Apply it by removing setters for fixed state and centralizing validation when updates remain necessary.
How it connects
Ties immutable instance variables to class invariants, mutators, constructors, accessors, and input validation.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 C++ Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.