Skip to content

C++ Variables and Initialization

Overview

Variables are named memory locations used to store data in programs. In C++, variables must be declared before use, and can be initialized at declaration or afterward. Correct variable declaration and initialization is the foundation of writing reliable C++ programs.

📝 Variable Declaration

Basic Variable Declaration Syntax

cpp
#include <iostream>
#include <string>

int main() {
    // Basic declaration syntax: type variable_name;
    int age;                    // Declare integer variable
    double price;               // Declare floating point variable
    char grade;                 // Declare character variable
    bool isActive;              // Declare boolean variable
    std::string name;           // Declare string variable
    
    // Multiple variable declarations
    int x, y, z;                // Declare three integer variables
    double length, width, height; // Declare three floating point variables
    
    // Mixed declaration and initialization
    int count = 0;              // Declare and initialize
    double ratio;               // Declaration only
    ratio = 1.5;               // Subsequent assignment
    
    std::cout << "Variable declaration completed" << std::endl;
    return 0;
}

Variable Naming Rules

cpp
#include <iostream>

int main() {
    // Valid variable names
    int age;                    // Start with letter
    int _private;               // Start with underscore
    int student_count;          // Contains underscore
    int value2;                 // Contains number
    int camelCaseVariable;      // Camel case naming
    int PascalCaseVariable;     // Pascal case naming
    
    // The following are invalid variable names (will cause compilation errors)
    // int 2age;                // Starts with number ❌
    // int student-count;       // Contains hyphen ❌
    // int int;                 // Keyword ❌
    // int student count;       // Contains space ❌
    
    // Naming convention suggestions
    int studentAge;             // Camel case naming (recommended)
    int student_age;            // Underscore naming
    const int MAX_SIZE = 100;   // Constants use all caps
    
    return 0;
}

🔧 Variable Initialization

Initialization Methods

cpp
#include <iostream>
#include <string>
#include <vector>

int main() {
    // 1. Copy Initialization
    int a = 42;
    double pi = 3.14159;
    std::string name = "Alice";
    
    // 2. Direct Initialization
    int b(42);
    double e(2.71828);
    std::string city("Beijing");
    
    // 3. Uniform/List Initialization (C++11)
    int c{42};
    double phi{1.618};
    std::string country{"China"};
    
    // 4. Default Initialization
    int d{};                    // Initialize to 0
    double f{};                 // Initialize to 0.0
    std::string empty{};        // Initialize to empty string
    
    // Output results
    std::cout << "Copy initialization: a = " << a << std::endl;
    std::cout << "Direct initialization: b = " << b << std::endl;
    std::cout << "Uniform initialization: c = " << c << std::endl;
    std::cout << "Default initialization: d = " << d << std::endl;
    
    return 0;
}

Advantages of List Initialization

cpp
#include <iostream>
#include <vector>

int main() {
    // Prevent narrowing conversion
    int x{3.14};                // Compilation error! Prevents double to int narrowing
    // int y = 3.14;            // Allowed but loses precision
    
    // Prevent Most Vexing Parse
    class Timer {
    public:
        Timer() { std::cout << "Timer created" << std::endl; }
    };
    
    Timer t1();                 // This is a function declaration, not an object definition!
    Timer t2{};                 // This is the object definition
    
    // Container initialization
    std::vector<int> numbers{1, 2, 3, 4, 5};
    std::vector<int> zeros(5);          // 5 zeros
    std::vector<int> fives{5};          // One element: 5
    std::vector<int> five_zeros{5, 0};  // 5 zeros
    
    std::cout << "numbers size: " << numbers.size() << std::endl;
    std::cout << "zeros size: " << zeros.size() << std::endl;
    std::cout << "fives size: " << fives.size() << std::endl;
    
    return 0;
}

🎯 auto Keyword (C++11)

Automatic Type Deduction

cpp
#include <iostream>
#include <vector>
#include <map>
#include <string>

int main() {
    // Basic auto usage
    auto integer = 42;              // int
    auto floating = 3.14;           // double
    auto character = 'A';           // char
    auto text = "Hello";            // const char*
    auto flag = true;               // bool
    
    // Complex type auto
    std::vector<int> numbers{1, 2, 3, 4, 5};
    auto iter = numbers.begin();    // std::vector<int>::iterator
    
    std::map<std::string, int> ages{{"Alice", 25}, {"Bob", 30}};
    auto pair = ages.find("Alice"); // std::map<std::string, int>::iterator
    
    // auto with function return values
    auto result = std::max(10, 20); // int
    
    // Output type information (needs typeid)
    std::cout << "integer type: " << typeid(integer).name() << std::endl;
    std::cout << "floating type: " << typeid(floating).name() << std::endl;
    
    return 0;
}

auto Limitations and Considerations

cpp
#include <iostream>
#include <vector>

// auto cannot be used for function parameters (before C++20)
// void func(auto param) { }  // Error! Not supported before C++20

// auto can be used for function return values (C++14)
auto getDouble() -> double {
    return 3.14;
}

// C++14 simplified syntax
auto getInteger() {
    return 42;
}

int main() {
    // auto must be initialized
    // auto x;                  // Error! Cannot deduce type
    auto y = 10;               // Correct
    
    // auto loses reference and const
    const int& ref = y;
    auto copy = ref;           // copy is int, not const int&
    auto& reference = ref;     // reference is const int&
    
    // auto with arrays
    int array[] = {1, 2, 3};
    auto ptr = array;          // ptr is int*, not int[]
    auto& arr_ref = array;     // arr_ref is int(&)[3]
    
    // auto with initializer list
    auto list = {1, 2, 3};     // std::initializer_list<int>
    // auto bad = {1, 2.0};    // Error! Inconsistent types
    
    std::cout << "auto deduction completed" << std::endl;
    return 0;
}

🔗 Reference Types

Lvalue References

cpp
#include <iostream>

void swapValues(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    // Reference declaration and initialization
    int original = 42;
    int& reference = original;  // reference is alias for original
    
    std::cout << "original: " << original << std::endl;
    std::cout << "reference: " << reference << std::endl;
    
    // Modify value through reference
    reference = 100;
    std::cout << "After modification original: " << original << std::endl;
    
    // References as function parameters
    int x = 10, y = 20;
    std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
    swapValues(x, y);
    std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
    
    // const reference
    const int& const_ref = original;
    // const_ref = 200;         // Error! Cannot modify const reference
    
    return 0;
}

Rvalue References (C++11)

cpp
#include <iostream>
#include <string>
#include <vector>

// Move constructor example
class MyString {
private:
    char* data_;
    size_t size_;
    
public:
    // Constructor
    MyString(const char* str = "") {
        size_ = strlen(str);
        data_ = new char[size_ + 1];
        strcpy(data_, str);
        std::cout << "Constructor: " << data_ << std::endl;
    }
    
    // Copy constructor
    MyString(const MyString& other) {
        size_ = other.size_;
        data_ = new char[size_ + 1];
        strcpy(data_, other.data_);
        std::cout << "Copy constructor: " << data_ << std::endl;
    }
    
    // Move constructor (C++11)
    MyString(MyString&& other) noexcept {
        size_ = other.size_;
        data_ = other.data_;        // Transfer ownership
        other.data_ = nullptr;      // Set source object to null
        other.size_ = 0;
        std::cout << "Move constructor: " << data_ << std::endl;
    }
    
    // Destructor
    ~MyString() {
        if (data_) {
            std::cout << "Destructor: " << data_ << std::endl;
            delete[] data_;
        }
    }
    
    const char* c_str() const { return data_; }
};

MyString createString() {
    return MyString("Temporary String");
}

int main() {
    // Lvalues and rvalues
    int x = 42;                 // x is lvalue, 42 is rvalue
    int& lref = x;              // Lvalue reference
    // int& bad = 42;           // Error! Cannot bind lvalue reference to rvalue
    
    // Rvalue reference
    int&& rref = 42;            // Correct! Rvalue reference can bind to rvalue
    int&& rref2 = std::move(x); // std::move converts lvalue to rvalue
    
    // Move semantics example
    std::cout << "=== Move Semantics Example ===" << std::endl;
    MyString str1 = createString();        // May trigger move constructor
    MyString str2 = std::move(str1);       // Explicitly use move constructor
    
    return 0;
}

📊 Static Variables and Global Variables

Global Variables

cpp
#include <iostream>

// Global variables
int global_counter = 0;
const double PI = 3.14159;

// Global function
void incrementCounter() {
    global_counter++;
}

int main() {
    std::cout << "Initial counter: " << global_counter << std::endl;
    
    incrementCounter();
    incrementCounter();
    
    std::cout << "Final counter: " << global_counter << std::endl;
    std::cout << "Pi: " << PI << std::endl;
    
    return 0;
}

Static Variables

cpp
#include <iostream>

// Static global variable (visible only in current file)
static int file_static_var = 100;

void demonstrateStaticLocal() {
    // Static local variable (maintains value between function calls)
    static int call_count = 0;
    call_count++;
    
    std::cout << "Function call count: " << call_count << std::endl;
}

class Counter {
private:
    static int total_objects_;  // Static member variable declaration
    int object_id_;
    
public:
    Counter() : object_id_(++total_objects_) {
        std::cout << "Created object #" << object_id_ << std::endl;
    }
    
    static int getTotalObjects() {  // Static member function
        return total_objects_;
    }
};

// Static member variable definition (must be defined outside class)
int Counter::total_objects_ = 0;

int main() {
    // Static local variable example
    demonstrateStaticLocal();
    demonstrateStaticLocal();
    demonstrateStaticLocal();
    
    // Static member variable example
    std::cout << "=== Static Member Example ===" << std::endl;
    Counter c1;
    Counter c2;
    Counter c3;
    
    std::cout << "Total objects: " << Counter::getTotalObjects() << std::endl;
    
    return 0;
}

🔄 Variable Lifetime and Scope

Scope Example

cpp
#include <iostream>

int global_var = 100;   // Global scope

void demonstrateScope() {
    int function_var = 200; // Function scope
    
    std::cout << "Access global variable in function: " << global_var << std::endl;
    
    {
        int block_var = 300; // Block scope
        std::cout << "Block variable: " << block_var << std::endl;
        std::cout << "Access function variable in block: " << function_var << std::endl;
        
        // Variable shadowing
        int global_var = 400; // Shadow same-named global variable
        std::cout << "Shadowed variable: " << global_var << std::endl;
        std::cout << "Access real global variable: " << ::global_var << std::endl;
    }
    
    // block_var not accessible here
    // std::cout << block_var; // Error!
}

int main() {
    demonstrateScope();
    
    // Loop scope
    for (int i = 0; i < 3; ++i) {
        int loop_var = i * 10;
        std::cout << "Loop variable: " << loop_var << std::endl;
    }
    // i and loop_var not accessible here
    
    return 0;
}

Object Lifetime

cpp
#include <iostream>

class LifetimeDemo {
private:
    std::string name_;
    
public:
    LifetimeDemo(const std::string& name) : name_(name) {
        std::cout << "Constructing object: " << name_ << std::endl;
    }
    
    ~LifetimeDemo() {
        std::cout << "Destructing object: " << name_ << std::endl;
    }
    
    void sayHello() const {
        std::cout << name_ << " says hello!" << std::endl;
    }
};

void demonstrateLifetime() {
    std::cout << "Entering function" << std::endl;
    
    LifetimeDemo obj1("Local Object");
    obj1.sayHello();
    
    {
        LifetimeDemo obj2("Block Object");
        obj2.sayHello();
    } // obj2 destructed here
    
    std::cout << "Before exiting function" << std::endl;
} // obj1 destructed here

int main() {
    std::cout << "=== Object Lifetime Demonstration ===" << std::endl;
    demonstrateLifetime();
    std::cout << "Function call ended" << std::endl;
    
    return 0;
}

🎨 Variable Initialization Best Practices

Initialization Strategies

cpp
#include <iostream>
#include <vector>
#include <string>
#include <memory>

int main() {
    // 1. Always initialize variables
    int count{0};               // Recommended: use list initialization
    double ratio{1.0};
    std::string name{"Default"};
    
    // 2. Use auto to simplify complex types
    auto numbers = std::vector<int>{1, 2, 3, 4, 5};
    auto ptr = std::make_unique<int>(42);
    
    // 3. const correctness
    const int MAX_SIZE{100};
    const auto& first_number = numbers[0]; // const reference avoids copying
    
    // 4. Declare as late as possible with appropriate initial value
    std::cout << "Please enter a number: ";
    int user_input;
    std::cin >> user_input;     // Declare and initialize when needed
    
    // 5. Use initializer list for aggregate initialization
    struct Point {
        double x, y;
    };
    Point origin{0.0, 0.0};
    Point point{3.0, 4.0};
    
    // 6. Avoid repeated initialization
    std::vector<int> large_vector;
    large_vector.reserve(1000); // Pre-allocate space to avoid multiple reallocations
    
    std::cout << "Initialization best practices demonstration completed" << std::endl;
    return 0;
}

Common Initialization Errors

cpp
#include <iostream>
#include <vector>

int main() {
    // Error 1: Uninitialized variable
    int uninitialized;          // Dangerous! Contains garbage value
    // std::cout << uninitialized; // Undefined behavior
    
    // Correct approach
    int initialized{0};         // Explicit initialization
    
    // Error 2: Most Vexing Parse
    class Widget {
    public:
        Widget() { std::cout << "Widget created" << std::endl; }
    };
    
    Widget w();                 // This is a function declaration, not an object definition!
    Widget w2{};                // Correct object definition
    
    // Error 3: Narrowing conversion
    // int narrow{3.14};        // Compilation error (good!)
    int safe = static_cast<int>(3.14); // Explicit conversion intention
    
    // Error 4: Forgetting const
    int mutable_value = 42;
    mutable_value = 100;        // Can modify, but may not be intended
    
    const int immutable_value{42}; // Better: explicitly indicate won't be modified
    
    std::cout << "Avoided common initialization errors" << std::endl;
    return 0;
}

📋 Variable Declaration Summary

Modern C++ Variable Declaration Guide

cpp
#include <iostream>
#include <vector>
#include <string>
#include <memory>

// Recommended variable declaration patterns
int main() {
    // 1. Prefer using auto and list initialization
    auto age{25};               // Clear integer
    auto name{"Alice"};         // String literal
    auto pi{3.14159};          // Floating point
    
    // 2. Use auto for complex types
    auto numbers = std::vector<int>{1, 2, 3, 4, 5};
    auto text = std::string{"Hello, World!"};
    auto ptr = std::make_unique<int>(42);
    
    // 3. const correctness
    const auto max_size{100};
    const auto& first_element = numbers.front();
    
    // 4. References to avoid copying
    for (const auto& number : numbers) {
        std::cout << number << " ";
    }
    std::cout << std::endl;
    
    // 5. Use static appropriately
    static auto call_count{0};
    ++call_count;
    std::cout << "Call count: " << call_count << std::endl;
    
    return 0;
}

Summary

Variable declaration and initialization is the foundation of C++ programming. Mastering correct methods improves code quality:

Key Points

  • Variable Declaration: Type + name, follow naming conventions
  • Initialization Methods: Copy, direct, list initialization each have their uses
  • auto Keyword: Simplify complex types, improve code readability
  • Reference Types: Lvalue references for aliases, rvalue references for move semantics
  • Scope and Lifetime: Understand variable visibility and existence time

Best Practices

  • Always initialize variables to avoid undefined behavior
  • Prefer list initialization to prevent narrowing conversions
  • Use auto to simplify complex type declarations
  • Follow const correctness principles
  • Declare variables in appropriate scope

Good variable management is the foundation of writing high-quality C++ code, laying a solid foundation for learning more advanced features later.

Content is for learning and research only.