liveBook · Manning

Validation method

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

A validation method is a function that checks an input value against required conditions and returns a valid value or reports failure.

A validation method checks an input against a class invariant and returns the accepted value or throws when the value is invalid. Without it, a constructor can accept bad data, or a built-in instance variable can remain without a meaningful value until later code assigns it. section 8.11 section 8.3

The mistake: validating in more than one place

A Circle that checks for a negative radius separately in its constructor and mutator duplicates the same knowledge. A later rule change can update one check but not the other. Move the rule into one private method: `static double validateRadius(double radius) { if (radius < 0) throw std::invalid_argument("radius is negative"); return radius; }`. The constructor can initialize with `radius(validateRadius(radius))`, while the mutator can assign `radius = validateRadius(r)`. Both entry points then preserve the invariant through the same check. section 8.3

For constructor parameters, call the validator in the initializer list rather than assigning first and checking later. A Person can therefore use `age(validateAge(age))`; a negative age throws before the object is established with an invalid value. Keeping the validator private localizes knowledge of the instance variable. section 8.11

Apply the pattern incrementally:

  • Identify the condition that defines a valid instance variable value. section 8.11
  • Put that condition in a private validation method that returns the value or throws an exception. section 8.11
  • Call the method from constructors and mutators instead of duplicating the check. section 8.3
  • Initialize every instance variable explicitly, preferably through the initializer list. section 8.11
Listing 8.23 Validating a parameter using a private validator in an initializer list
class Person {
private:
    std::string name;
    int age;
    static int validateAge(int age) {            #1
        if (age < 0)
            throw std::invalid_argument("negative age");
        return age;
    }
public:
    Person(const std::string& name, int age) : 
        name(name), age(validateAge(age)) {}     #2
    friend std::ostream& operator<<(std::ostream&, const Person&);
};

std::ostream& operator<<(std::ostream& o, const Person& p) {
    o << p.name << " is " << p.age << " years old";
    return o;
}

int main() {
    Person joey("Joey", 27);                     #3
    std::cout << joey << '\n';
    return 0;
}

Inspect the complete Person example, including the private age validator, initializer-list call, exception, and valid construction. section 8.11

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 Circle implementation that centralizes radius validation for both construction and mutation. section 8.3

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

How deep it goes
Demonstrates validation, exception throwing, initializer-list calls, and shared constructor–mutator logic, stopping short of broader error-handling design.
Examples
Uses Person age validation and Circle radius validation to contrast duplicated checks with a single validation source.
What it assumes
Assumes basic C++ classes and constructors while teaching explicit instance-variable initialization, class invariants, and parameter validation.
What you can do after
Build it from scratch by centralizing parameter checks and calling them from initializer lists, constructors, and mutators.
How it connects
Connects validation methods with class invariants, constructors, initializer lists, mutators, immutable instance variables, and exceptions.

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.