Classes, Structs and Member Initializer Lists
Object Oriented Programming and RAII

4.1 Classes, Structs and Member Initializer Lists

In C++, object oriented programming is defined by direct control over memory and construction efficiency. While languages like Java hide object instances behind references and garbage collection, C++ objects can live directly on the stack or be packed tightly into arrays.

Let's look at encapsulation, the default visibility rules, and how to initialize members without wasting performance.

Class vs Struct: The Only Difference

In C++, the difference between a class and a struct is purely default visibility. That is it. There is no other difference. An object instantiated from a struct can have constructors, virtual functions, and inheritance just like a class.

* class: Members and base classes are private by default.

* struct: Members and base classes are public by default.

We typically use struct for plain data carriers, and class when we want to enforce encapsulation (protecting internal states using private members).

Encapsulation (Hiding the Wires)

Encapsulation is the practice of keeping data members private and exposing only a clean public interface. This ensures other developers do not modify internal states directly and cause bugs.

* The High Voltage Metaphor: Think of encapsulation like the outer shell of an electrical machine. The dangerous, high voltage wiring is hidden inside (private data members). The user is only exposed to a green start button and a dial (public methods). If you remove the shell and let users touch the wires directly, they will fry the machine (corrupt the object state) and crash the system.

The Constructor Double Initialization Pitfall

When you write a constructor and assign values to members inside the constructor body, you are wasting CPU cycles. Before the code inside the constructor braces runs, C++ automatically initializes all member variables using their default constructors first! When you assign values inside the braces, you overwrite those defaults. This is double initialization.

To avoid this, modern C++ uses Member Initializer Lists (also known as constructor initialization lists). This initializes members directly with their target values, performing only one initialization step:

#include <string>

// Inefficient: Name is first default initialized, then assigned n
class BadPlayer {
    std::string name;
public:
    BadPlayer(std::string n) {
        name = n;
    }
};

//  Optimal: Name is initialized directly with n using initializer list
class GoodPlayer {
    std::string name;
public:
    GoodPlayer(std::string n) : name{n} {} // Direct initialization!
};
Double initialization vs member initializer lists.

Member initializers also execute in the order the variables are declared in the class, not the order you write them in the list. Always match your list order to your declaration order to prevent compiler warnings.

The Declaration Order Trap

This ordering rule is not just about clean style; it is a critical safety feature. If you use one member variable to initialize another member variable in the list, and they are declared in the wrong order in the class body, you will initialize the target using uninitialized garbage memory!

#include <iostream>

class InitTrap {
    int a; // a is declared FIRST!
    int b; // b is declared SECOND!
public:
    // Even though you wrote b{x} first in the list, a{b + 5} runs first!
    // Since b has not been initialized yet, b contains garbage memory during a's initialization.
    InitTrap(int x) : b{x}, a{b + 5} {}

    void printValues() {
        std::cout << "a: " << a << ", b: " << b << '\n';
    }
};

int main() {
    InitTrap test{10};
    test.printValues(); // Prints: a = random garbage number, b = 10!
    return 0;
}
The declaration order initialization bug trap.
Finished reading this lesson?