C++ Zero Initialization and Value Initialization
In C++, properly initializing variables and objects is vital for
avoiding undefined behavior caused by indeterminate values. Zero
initialization is a foundational process that sets the initial value of
an object to zero, representing null pointers, boolean
false, and zero-valued scalars. Value initialization is a
broader syntax-driven initialization form—commonly invoked using empty
parentheses () or braces {}—that
systematically triggers zero initialization for scalar types,
aggregates, and certain class types. This article breaks down what zero
initialization is, explains how value initialization works, and details
the exact conditions under which value initialization invokes zero
initialization.
What is Zero Initialization?
Zero initialization sets the storage of an object to a conceptual zero representation:
- Scalar types: Integer types become
0, floating-point types become+0.0, boolean types becomefalse, and pointer types become a null pointer value of that type. - Class types: All padding bits are initialized to zero, all non-static data members are zero-initialized, and all base class subobjects are zero-initialized.
- Array types: Each element in the array is zero-initialized.
- Reference types: References cannot be zero-initialized.
Zero initialization occurs implicitly for variables with static or thread-local storage duration before any other initialization takes place. For automatic (local stack) variables, zero initialization is not applied by default, leaving non-class types with indeterminate values unless explicitly initialized.
What is Value Initialization?
Value initialization was introduced in C++03 and refined in C++11 with list initialization. It occurs when an object is initialized with an empty set of parentheses or empty braces:
T obj(); // Note: can trigger the "most vexing parse" if mistaken for a function declaration
T obj{}; // Value initialization (uniform syntax, avoids most vexing parse)
T* ptr = new T();
T* ptr = new T{};Unlike default initialization (such as int x;), which
leaves primitive types on the stack uninitialized, value initialization
guarantees a well-defined initial state.
How Value Initialization Invokes Zero Initialization
The C++ standard defines the behavior of value initialization based on the type category and constructor presence of the target object. Value initialization directly or indirectly invokes zero initialization in the following scenarios:
1. Scalar Types
When value-initializing any scalar type (such as int,
double, bool, or raw pointers), the process
directly performs zero initialization.
int a{}; // Zero-initialized: a == 0
double b = double(); // Zero-initialized: b == 0.0
char* p{}; // Zero-initialized: p == nullptr2. Class Types Without User-Provided Constructors
For a class or struct with no user-provided default constructor (such
as aggregate structs or classes relying on an implicitly defined or
defaulted = default constructor):
- The object is first completely zero-initialized.
- If the class has a non-trivial default constructor, that constructor is then executed.
This rule ensures that any padding bits and data members without in-class initializers are wiped to zero before member initialization logic runs.
struct Point {
int x;
int y;
};
Point p{}; // Value-initialized: zero-initializes p.x and p.y to 0If Point had declared a user-provided constructor (e.g.,
Point() { /* empty */ }), value initialization would simply
call that constructor instead of zero-initializing members, leaving
x and y uninitialized unless explicitly set in
the constructor.
3. Arrays
When an array is value-initialized, every individual element in the array is value-initialized. If the elements are scalar types or classes without user-provided constructors, each element is zero-initialized recursively.
int buffer[100]{}; // All 100 integers are zero-initialized to 04. Class Members in Member Initializer Lists
When an aggregate or non-aggregate class uses a member initializer list with an empty set of braces or parentheses, that specific member undergoes value initialization, which in turn invokes zero initialization for scalars and eligible structs:
class Account {
int id;
double balance;
public:
Account() : id{}, balance{} {} // Both members undergo value-init -> zero-init
};Summary of the Relationship
Value initialization serves as the high-level syntax that requests safe, empty initialization. When the target entity has no custom initialization logic defined by the programmer—such as built-in scalars, standard aggregates, and classes with implicitly defined constructors—the C++ compiler fulfills the value initialization request by executing zero initialization.