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++代码的基础,为后续学习更高级特性打下坚实基础。