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 ❌0 ✅
double (local)Indeterminate ❌0.0 ✅
pointer (local)Indeterminate ❌nullptr ✅
int (static)0 ✅0 ✅
Class with ctorCalls ctor ✅Calls ctor ✅
Class without ctorMembers indeterminate ❌Members zeroed ✅

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
// Interview answer:
// "Value initialization with empty braces {} guarantees
// zero-initialization for fundamentals (0, 0.0, nullptr) and
// calls default constructor for classes. Default initialization
// leaves local fundamentals indeterminate (UB if read). For
// classes without constructors, value init zeros members while
// default leaves them indeterminate. Always prefer {} for safety
// unless immediately assigning a value. Tiny performance cost
// is worth preventing undefined behavior."