liveBook · Manning

Initialization

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

Initialization is the process of giving a C++ object its initial value or state when it is created.

Initialization gives an object a defined value or state when it is created. Declaring everything at the top of a function encourages premature defaults, hides intended use, and can produce wrong results or stale assumptions. Declare and initialize variables at the point of need instead. section 5.1

Listing 5.1 Declaring variables at the top of the function
int maximum(const std::vector<int>& values) {
    int max = 0;                                           #1
    int pos;                                               #2
 
    // assume code here that uses pos ...
    pos = 1;                                               #3
    // assume more code here...
    for (; pos < values.size(); ++pos)                     #4
        if (values[pos] > max)
            max = values[pos];
    return max;
}
 
int main() {
    std::vector<int> values;
    values.push_back(1);
    values.push_back(-2);
    values.push_back(-3);
    std::cout << maximum(values) << '\n';
    return 0;
}

The problematic maximum function initializes max to zero, which returns the wrong result when all values are negative, and leaves pos exposed to unrelated changes before the loop. section 5.1

Initialize every built-in instance variable before its first read. An uninitialized value has an undefined state; assigning it before reading is safe, but relying on memory contents is not. Constructors should establish the class invariant rather than creating objects that require later repair. section 8.11

Listing 8.21 A class with an uninitialized built-in instance variable
class Person {
private:
    std::string name;                                         #1
    int age;                                                  #2
public:
    Person(const std::string& name) { this->name = name; }    #3
    void setAge(int age) { this->age = age; }
    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");
    std::cout << joey << '\n';
    return 0;
}

Person assigns name but leaves age uninitialized, so printing a newly created object reads undefined data. section 8.11

Listing 8.22 A class that insists on arguments for every instance variable
class Person {
private:
    std::string name;
    int age;
public:
    Person(const std::string& name, int age) : 
        name(name), age(age) {}                  #1
    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);                     #2
    std::cout << joey << '\n';
    return 0;
}

Require all essential state in the constructor and initialize members in the initializer list, making an incomplete Person impossible through this interface. section 8.11

Validate constructor arguments while initializing members. Throwing when construction cannot establish a valid object prevents partially initialized instances and avoids validity flags that callers may forget to check. section 8.11 section 10.2

Listing 10.2 Throwing exceptions for construction failures
class Person {
private:
    std::string name;
    int age;
    static const std::string& validateName(const std::string& name) { #1
        if (name.empty())
            throw std::invalid_argument("name must not be empty");
        return name;
    }
    static int validateAge(int age) {                                 #2
        if (age < 0)
            throw std::out_of_range("age must be non-negative");
        return age;
    }
public:
    Person(const std::string& name, int age) : 
       name(validateName(name)), age(validateAge(age)) {}             #3
    int getAge() const { return age; }
    const std::string& getName() const { return name; }
    bool isValid() const { return age > -1; } 
};
 
int main() {
    // Person sally("Sally");                                         #4
    Person sally("Sally", 27);
    std::cout << sally.getName() << " is " << sally.getAge() 
        << " years old\n";
 
    Person brian("Brian", -1);                                        #5
    std::cout << brian.getName() << " is " << brian.getAge() 
        << " years old\n";
    return 0;
}

The constructor validates name and age and throws for invalid input, so successful construction produces a usable Person instead of a repairable partial object. section 10.2

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

How deep it goes
Explains declaration placement, primitive initialization, constructor initializer lists, validation, and exception-based construction, stopping short of broader language mechanics.
Examples
Demonstrates maximum over negative values, an uninitialized Person age, required constructor arguments, and validated Person construction.
What you can do after
Build it from scratch by placing initialization near use, requiring constructor state, validating arguments, and rejecting failed construction.
How it connects
Connects initialization with constructors, class invariants, undefined behavior, initializer lists, and exceptions.

Also explained in

Compare how 5 books explain initialization →

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.