Skip to content

C++ 变量和初始化

概述

变量是程序中用于存储数据的命名内存位置。在C++中,变量必须在使用前声明,并可以在声明时或之后进行初始化。正确的变量声明和初始化是编写可靠C++程序的基础。

📝 变量声明

基本变量声明语法

cpp
#include <iostream>
#include <string>

int main() {
    // 基本声明语法: 类型 变量名;
    int age;                    // 声明整数变量
    double price;               // 声明浮点数变量
    char grade;                 // 声明字符变量
    bool isActive;              // 声明布尔变量
    std::string name;           // 声明字符串变量
    
    // 多个变量声明
    int x, y, z;                // 声明三个整数变量
    double length, width, height; // 声明三个浮点数变量
    
    // 混合声明和初始化
    int count = 0;              // 声明并初始化
    double ratio;               // 只声明
    ratio = 1.5;               // 后续赋值
    
    std::cout << "变量声明完成" << std::endl;
    return 0;
}

变量命名规则

cpp
#include <iostream>

int main() {
    // 有效的变量名
    int age;                    // 字母开头
    int _private;               // 下划线开头
    int student_count;          // 包含下划线
    int value2;                 // 包含数字
    int camelCaseVariable;      // 驼峰命名
    int PascalCaseVariable;     // 帕斯卡命名
    
    // 以下是无效的变量名(会导致编译错误)
    // int 2age;                // 数字开头 ❌
    // int student-count;       // 包含连字符 ❌
    // int int;                 // 关键字 ❌
    // int student count;       // 包含空格 ❌
    
    // 命名约定建议
    int studentAge;             // 驼峰命名法(推荐)
    int student_age;            // 下划线命名法
    const int MAX_SIZE = 100;   // 常量使用全大写
    
    return 0;
}

🔧 变量初始化

初始化方式

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. 默认初始化
    int d{};                    // 初始化为0
    double f{};                 // 初始化为0.0
    std::string empty{};        // 初始化为空字符串
    
    // 输出结果
    std::cout << "复制初始化: a = " << a << std::endl;
    std::cout << "直接初始化: b = " << b << std::endl;
    std::cout << "统一初始化: c = " << c << std::endl;
    std::cout << "默认初始化: d = " << d << std::endl;
    
    return 0;
}

列表初始化的优势

cpp
#include <iostream>
#include <vector>

int main() {
    // 防止窄化转换
    int x{3.14};                // 编译错误!防止double到int的窄化转换
    // int y = 3.14;            // 允许但会丢失精度
    
    // 防止最令人困惑的解析(Most Vexing Parse)
    class Timer {
    public:
        Timer() { std::cout << "Timer created" << std::endl; }
    };
    
    Timer t1();                 // 这是函数声明,不是对象定义!
    Timer t2{};                 // 这才是对象定义
    
    // 容器初始化
    std::vector<int> numbers{1, 2, 3, 4, 5};
    std::vector<int> zeros(5);          // 5个0
    std::vector<int> fives{5};          // 一个元素5
    std::vector<int> five_zeros{5, 0};  // 5个0
    
    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关键字 (C++11)

自动类型推导

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

int main() {
    // 基本auto用法
    auto integer = 42;              // int
    auto floating = 3.14;           // double
    auto character = 'A';           // char
    auto text = "Hello";            // const char*
    auto flag = true;               // bool
    
    // 复杂类型的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与函数返回值
    auto result = std::max(10, 20); // int
    
    // 输出类型信息(需要typeid)
    std::cout << "integer type: " << typeid(integer).name() << std::endl;
    std::cout << "floating type: " << typeid(floating).name() << std::endl;
    
    return 0;
}

auto的限制和注意事项

cpp
#include <iostream>
#include <vector>

// auto不能用于函数参数(C++20之前)
// void func(auto param) { }  // 错误!C++20之前不支持

// auto可以用于函数返回值(C++14)
auto getDouble() -> double {
    return 3.14;
}

// C++14简化语法
auto getInteger() {
    return 42;
}

int main() {
    // auto必须初始化
    // auto x;                  // 错误!无法推导类型
    auto y = 10;               // 正确
    
    // auto会丢失引用和const
    const int& ref = y;
    auto copy = ref;           // copy是int,不是const int&
    auto& reference = ref;     // reference是const int&
    
    // auto与数组
    int array[] = {1, 2, 3};
    auto ptr = array;          // ptr是int*,不是int[]
    auto& arr_ref = array;     // arr_ref是int(&)[3]
    
    // auto与初始化列表
    auto list = {1, 2, 3};     // std::initializer_list<int>
    // auto bad = {1, 2.0};    // 错误!类型不一致
    
    std::cout << "auto推导完成" << std::endl;
    return 0;
}

🔗 引用类型

左值引用

cpp
#include <iostream>

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

int main() {
    // 引用声明和初始化
    int original = 42;
    int& reference = original;  // reference是original的别名
    
    std::cout << "original: " << original << std::endl;
    std::cout << "reference: " << reference << std::endl;
    
    // 通过引用修改值
    reference = 100;
    std::cout << "修改后 original: " << original << std::endl;
    
    // 引用用于函数参数
    int x = 10, y = 20;
    std::cout << "交换前: x = " << x << ", y = " << y << std::endl;
    swapValues(x, y);
    std::cout << "交换后: x = " << x << ", y = " << y << std::endl;
    
    // const引用
    const int& const_ref = original;
    // const_ref = 200;         // 错误!不能修改const引用
    
    return 0;
}

右值引用 (C++11)

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

// 移动构造函数示例
class MyString {
private:
    char* data_;
    size_t size_;
    
public:
    // 构造函数
    MyString(const char* str = "") {
        size_ = strlen(str);
        data_ = new char[size_ + 1];
        strcpy(data_, str);
        std::cout << "构造函数: " << data_ << std::endl;
    }
    
    // 拷贝构造函数
    MyString(const MyString& other) {
        size_ = other.size_;
        data_ = new char[size_ + 1];
        strcpy(data_, other.data_);
        std::cout << "拷贝构造函数: " << data_ << std::endl;
    }
    
    // 移动构造函数(C++11)
    MyString(MyString&& other) noexcept {
        size_ = other.size_;
        data_ = other.data_;        // 转移所有权
        other.data_ = nullptr;      // 置空源对象
        other.size_ = 0;
        std::cout << "移动构造函数: " << data_ << std::endl;
    }
    
    // 析构函数
    ~MyString() {
        if (data_) {
            std::cout << "析构函数: " << data_ << std::endl;
            delete[] data_;
        }
    }
    
    const char* c_str() const { return data_; }
};

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

int main() {
    // 左值和右值
    int x = 42;                 // x是左值,42是右值
    int& lref = x;              // 左值引用
    // int& bad = 42;           // 错误!不能将左值引用绑定到右值
    
    // 右值引用
    int&& rref = 42;            // 正确!右值引用可以绑定到右值
    int&& rref2 = std::move(x); // std::move将左值转换为右值
    
    // 移动语义示例
    std::cout << "=== 移动语义示例 ===" << std::endl;
    MyString str1 = createString();        // 可能触发移动构造
    MyString str2 = std::move(str1);       // 明确使用移动构造
    
    return 0;
}

📊 静态变量和全局变量

全局变量

cpp
#include <iostream>

// 全局变量
int global_counter = 0;
const double PI = 3.14159;

// 全局函数
void incrementCounter() {
    global_counter++;
}

int main() {
    std::cout << "初始计数器: " << global_counter << std::endl;
    
    incrementCounter();
    incrementCounter();
    
    std::cout << "最终计数器: " << global_counter << std::endl;
    std::cout << "圆周率: " << PI << std::endl;
    
    return 0;
}

静态变量

cpp
#include <iostream>

// 静态全局变量(仅在当前文件可见)
static int file_static_var = 100;

void demonstrateStaticLocal() {
    // 静态局部变量(保持值在函数调用之间)
    static int call_count = 0;
    call_count++;
    
    std::cout << "函数调用次数: " << call_count << std::endl;
}

class Counter {
private:
    static int total_objects_;  // 静态成员变量声明
    int object_id_;
    
public:
    Counter() : object_id_(++total_objects_) {
        std::cout << "创建对象 #" << object_id_ << std::endl;
    }
    
    static int getTotalObjects() {  // 静态成员函数
        return total_objects_;
    }
};

// 静态成员变量定义(必须在类外定义)
int Counter::total_objects_ = 0;

int main() {
    // 静态局部变量示例
    demonstrateStaticLocal();
    demonstrateStaticLocal();
    demonstrateStaticLocal();
    
    // 静态成员变量示例
    std::cout << "=== 静态成员示例 ===" << std::endl;
    Counter c1;
    Counter c2;
    Counter c3;
    
    std::cout << "总对象数: " << Counter::getTotalObjects() << std::endl;
    
    return 0;
}

🔄 变量生命周期和作用域

作用域示例

cpp
#include <iostream>

int global_var = 100;   // 全局作用域

void demonstrateScope() {
    int function_var = 200; // 函数作用域
    
    std::cout << "函数内访问全局变量: " << global_var << std::endl;
    
    {
        int block_var = 300; // 块作用域
        std::cout << "块内变量: " << block_var << std::endl;
        std::cout << "块内访问函数变量: " << function_var << std::endl;
        
        // 变量遮蔽
        int global_var = 400; // 遮蔽同名全局变量
        std::cout << "遮蔽后的变量: " << global_var << std::endl;
        std::cout << "访问真正的全局变量: " << ::global_var << std::endl;
    }
    
    // block_var在这里不可访问
    // std::cout << block_var; // 错误!
}

int main() {
    demonstrateScope();
    
    // 循环作用域
    for (int i = 0; i < 3; ++i) {
        int loop_var = i * 10;
        std::cout << "循环变量: " << loop_var << std::endl;
    }
    // i和loop_var在这里都不可访问
    
    return 0;
}

对象生命周期

cpp
#include <iostream>

class LifetimeDemo {
private:
    std::string name_;
    
public:
    LifetimeDemo(const std::string& name) : name_(name) {
        std::cout << "构造对象: " << name_ << std::endl;
    }
    
    ~LifetimeDemo() {
        std::cout << "析构对象: " << name_ << std::endl;
    }
    
    void sayHello() const {
        std::cout << name_ << " says hello!" << std::endl;
    }
};

void demonstrateLifetime() {
    std::cout << "进入函数" << std::endl;
    
    LifetimeDemo obj1("局部对象");
    obj1.sayHello();
    
    {
        LifetimeDemo obj2("块对象");
        obj2.sayHello();
    } // obj2在这里被析构
    
    std::cout << "退出函数前" << std::endl;
} // obj1在这里被析构

int main() {
    std::cout << "=== 对象生命周期演示 ===" << std::endl;
    demonstrateLifetime();
    std::cout << "函数调用结束" << std::endl;
    
    return 0;
}

🎨 变量初始化最佳实践

初始化策略

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

int main() {
    // 1. 总是初始化变量
    int count{0};               // 推荐:使用列表初始化
    double ratio{1.0};
    std::string name{"Default"};
    
    // 2. 使用auto简化复杂类型
    auto numbers = std::vector<int>{1, 2, 3, 4, 5};
    auto ptr = std::make_unique<int>(42);
    
    // 3. const正确性
    const int MAX_SIZE{100};
    const auto& first_number = numbers[0]; // const引用避免拷贝
    
    // 4. 尽可能延迟声明直到有合适的初始值
    std::cout << "请输入一个数字: ";
    int user_input;
    std::cin >> user_input;     // 在需要时才声明和初始化
    
    // 5. 使用初始化列表进行聚合初始化
    struct Point {
        double x, y;
    };
    Point origin{0.0, 0.0};
    Point point{3.0, 4.0};
    
    // 6. 避免重复初始化
    std::vector<int> large_vector;
    large_vector.reserve(1000); // 预分配空间避免多次重新分配
    
    std::cout << "初始化最佳实践演示完成" << std::endl;
    return 0;
}

常见初始化错误

cpp
#include <iostream>
#include <vector>

int main() {
    // 错误1:未初始化的变量
    int uninitialized;          // 危险!包含垃圾值
    // std::cout << uninitialized; // 未定义行为
    
    // 正确做法
    int initialized{0};         // 明确初始化
    
    // 错误2:最令人困惑的解析
    class Widget {
    public:
        Widget() { std::cout << "Widget created" << std::endl; }
    };
    
    Widget w();                 // 这是函数声明,不是对象定义!
    Widget w2{};                // 正确的对象定义
    
    // 错误3:窄化转换
    // int narrow{3.14};        // 编译错误(好事!)
    int safe = static_cast<int>(3.14); // 明确的转换意图
    
    // 错误4:忘记const
    int mutable_value = 42;
    mutable_value = 100;        // 可以修改,但可能不是本意
    
    const int immutable_value{42}; // 更好:明确表示不会修改
    
    std::cout << "避免了常见初始化错误" << std::endl;
    return 0;
}

📋 变量声明总结

现代C++变量声明指南

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

// 推荐的变量声明模式
int main() {
    // 1. 优先使用auto和列表初始化
    auto age{25};               // 清晰的整数
    auto name{"Alice"};         // 字符串字面量
    auto pi{3.14159};          // 浮点数
    
    // 2. 对于复杂类型使用auto
    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正确性
    const auto max_size{100};
    const auto& first_element = numbers.front();
    
    // 4. 引用用于避免拷贝
    for (const auto& number : numbers) {
        std::cout << number << " ";
    }
    std::cout << std::endl;
    
    // 5. 合理使用static
    static auto call_count{0};
    ++call_count;
    std::cout << "调用次数: " << call_count << std::endl;
    
    return 0;
}

总结

变量声明和初始化是C++编程的基础,掌握正确的方法能提高代码质量:

关键要点

  • 变量声明:类型 + 名称,遵循命名规范
  • 初始化方式:复制、直接、列表初始化各有用途
  • auto关键字:简化复杂类型,提高代码可读性
  • 引用类型:左值引用用于别名,右值引用支持移动语义
  • 作用域和生命周期:理解变量的可见性和存在时间

最佳实践

  • 总是初始化变量,避免未定义行为
  • 优先使用列表初始化,防止窄化转换
  • 合理使用auto简化复杂类型声明
  • 遵循const正确性原则
  • 在适当的作用域声明变量

良好的变量管理是编写高质量C++代码的基础,为后续学习更高级特性打下坚实基础。

本站内容仅供学习和研究使用。