Value Initialization
Explicitly request zero-initialization for fundamentals, default construction for classes. Safer than default initialization.
Value initialization ensures fundamentals are zeroed, not indeterminate. Always safe to read.
Syntax
Empty parentheses or braces trigger value initialization.
int x{}; // 0
int y = int(); // 0
double d{}; // 0.0
bool b{}; // false
int* ptr{}; // nullptr
std::cout << x; // Safe: 0
std::cout << ptr; // Safe: nullptr
Key: Zero-initialization happens before any construction.
Fundamental Types
void function() {
// Default initialization (dangerous)
int a; // Indeterminate
double b; // Indeterminate
char* c; // Indeterminate
// Value initialization (safe)
int x{}; // 0
double y{}; // 0.0
char* z{}; // nullptr
}
Comparison Table
| Type | Default Init | Value Init |
|---|---|---|
int (local) | Indeterminate No | 0 Yes |
double (local) | Indeterminate No | 0.0 Yes |
pointer (local) | Indeterminate No | nullptr Yes |
int (static) | 0 Yes | 0 Yes |
| Class with ctor | Calls ctor Yes | Calls ctor Yes |
| Class without ctor | Members indeterminate No | Members zeroed Yes |
Class Types
With User-Defined Constructor
class Widget {
int value;
public:
Widget() : value(42) {}
};
Widget w1; // Default init → calls Widget()
Widget w2{}; // Value init → also calls Widget()
// Identical when constructor exists
Rule: User-defined constructor = same behavior for both.
Without User-Defined Constructor
This is where value initialization shines.
struct Point {
int x, y;
// No constructor
};
Point p1; // Default init: x, y indeterminate
Point p2{}; // Value init: x=0, y=0
std::cout << p1.x; // UB
std::cout << p2.x; // Safe: 0
Benefit: Simple aggregates safely zeroed with {}.
Arrays
int arr1[5]; // Indeterminate
int arr2[5]{}; // All zeros
std::cout << arr1[0]; // UB
std::cout << arr2[0]; // Safe: 0
// Multi-dimensional
int matrix[3][3]{}; // All zeros
Dynamic Allocation
int* p1 = new int; // Indeterminate
int* p2 = new int(); // 0
int* p3 = new int{}; // 0
std::cout << *p1; // UB
std::cout << *p2; // Safe: 0
delete p1; delete p2; delete p3;
// Arrays
int* arr1 = new int[10]; // Indeterminate
int* arr2 = new int[10](); // All zeros
int* arr3 = new int[10]{}; // All zeros
delete[] arr1; delete[] arr2; delete[] arr3;
Critical: Always use () or {} with new for fundamentals.
Member Initialization
class Container {
int value;
double ratio;
int* ptr;
public:
Container()
: value{}, // 0
ratio{}, // 0.0
ptr{} // nullptr
{}
};
Pattern: Value-initialize all members for guaranteed valid state.
Quick Decision Guide
Performance
void compare() {
// Default: no cost, but dangerous
int arr1[1000000]; // Instant, but garbage
// Value: small cost, but safe
int arr2[1000000]{}; // ~microseconds, all zeros
}
Reality: Cost is negligible vs safety benefit. Modern CPUs efficiently zero memory.
When to Use Each
| Scenario | Use |
|---|---|
| Local fundamental | Value init {} (safe) |
| Immediately assigning | Default init (performance) |
| Class with ctor | Either (same result) |
| Aggregate | Value init {} (zeros members) |
| Dynamic allocation | Value init () or {} |
Common Patterns
// Safe patterns
int x{}; // Zero
int* p = new int{}; // Zero on heap
int arr[10]{}; // All zeros
struct Point { int x, y; };
Point p{}; // x=0, y=0
// Dangerous patterns
int x; // Indeterminate
int* p = new int; // Indeterminate
int arr[10]; // All indeterminate
Point p; // x, y indeterminate
Summary
Guaranteed Safety:
- Fundamentals: Always zero (0, 0.0, false, nullptr)
- Classes with constructor: Calls default constructor
- Classes without constructor: All members zeroed
- Arrays: All elements zeroed
Syntax Triggers:
- Empty braces:
int x{}; - Empty parentheses:
int x = int(); - With
new:new int()ornew int{} - Member init:
value{}in initializer list
vs Default Initialization:
- Default (local): Indeterminate
- Value: Always zero
- Classes with ctor: Same behavior
- Classes without ctor: Value zeros members
When to Use:
- Default choice for fundamentals (always safe)
- Dynamic allocation:
new int{}notnew int - Member initialization for guaranteed valid state
- Aggregates without constructors
Performance:
- Tiny cost (~nanoseconds) to zero memory
- Modern CPUs optimize zeroing efficiently
- Safety benefit far outweighs minimal cost