Skip to main content

Value Initialization

Explicitly request zero-initialization for fundamentals, default construction for classes. Safer than default initialization.

Guaranteed Zero

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

TypeDefault InitValue Init
int (local)Indeterminate No0 Yes
double (local)Indeterminate No0.0 Yes
pointer (local)Indeterminate Nonullptr Yes
int (static)0 Yes0 Yes
Class with ctorCalls ctor YesCalls ctor Yes
Class without ctorMembers indeterminate NoMembers 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

ScenarioUse
Local fundamentalValue init {} (safe)
Immediately assigningDefault init (performance)
Class with ctorEither (same result)
AggregateValue init {} (zeros members)
Dynamic allocationValue 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

Value Initialization - Key Points

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() or new 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{} not new 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