C++ 变量初始化方式总结 | 拷贝初始化 | 列表初始化 | 值初始化
1. 拷贝初始化
int a = 1;
std::string s = "hello";
最经典、最普遍的写法
类类型会调用构造函数进行“拷贝构造”(可能优化为直接构造)
存在隐式类型转换风险
2. 直接初始化
int a(1);
std::string s("hello");
使用括号直接初始化变量
对类对象来说通常更高效
构造函数如果加了
explicit
,只能使用此方式
3. 列表初始化
示例:
int a{10}; // 初始化为 10
std::string s{"hello"}; // 初始化字符串
std::vector<int> v{1, 2, 3}; // 初始化容器
列表初始化的几种形式
写法 | 名称 | 示例 |
---|---|---|
T var{val}; | 直接列表初始化 | int a{10}; |
T var = {val}; | 拷贝列表初始化 | int b = {20}; |
T var{}; | 值初始化(默认) | int c{}; // 0 |
列表初始化的优点
1. 防止窄化转换
int a{3.14}; // ❌ 编译错误:不能将 double 隐式转换为 int
int b = 3.14; // ✅ 会自动截断为 3(但可能有隐患)
所以 {}
更安全,防止精度丢失
2. 支持类类型初始化
class Person {
public:Person(int age, std::string name) { ... }
};Person p1{18, "Tom"}; // 列表初始化成员变量
简洁、清晰
支持构造函数匹配
3. 可以初始化数组、容器等聚合类型
int arr[] = {1, 2, 3}; // 数组
std::vector<int> vec{1, 2, 3}; // 容器
std::tuple<int, std::string>{1, "a"}; // 其他聚合结构
4. 原理:列表初始化本质调用的是 initializer_list
构造函数
当你使用花括号 {}
来初始化一个类对象时,编译器会尝试匹配下面这些可能的构造函数:
最优先:
MyArray(std::initializer_list<T>)
然后尝试普通构造函数:
MyArray(int, int)
等
所以,要让你的 MyArray<T>
支持:
MyArray<int> arr{1, 2, 3};
你必须提供:
MyArray(std::initializer_list<T> list)
示例:手写一个支持列表初始化的 MyArray
类模板
#include <iostream>
#include <initializer_list>template<typename T>
class MyArray {
private:T* pData;std::size_t m_Size;public:// 构造函数:支持 std::initializer_listMyArray(std::initializer_list<T> list): pData(new T[list.size()]),m_Size(list.size()) {std::size_t i = 0;for (const auto& item : list) {pData[i++] = item;}std::cout << "initializer_list 构造函数调用" << std::endl;}// 析构函数~MyArray() {delete[] pData;}// 打印函数void print() const {for (std::size_t i = 0; i < m_Size; ++i) {std::cout << pData[i] << " ";}std::cout << std::endl;}
};
使用示例
int main() {MyArray<int> arr{10, 20, 30, 40};arr.print(); // 输出:10 20 30 40return 0;
}
解析:std::initializer_list<T>
是什么?
它是一个 轻量容器,用于存储一系列值。
实际上,它内部只是一个指向数组的指针 + 元素个数。
提供
.begin()
、.end()
迭代器,可以用于范围 for 循环。
std::initializer_list<int> list = {1, 2, 3};
for (int v : list) {std::cout << v << std::endl;
}
注意事项
项目 | 说明 |
---|---|
只能作为构造函数参数 | initializer_list 不能直接赋值,只能构造或拷贝 |
只能接受同一类型元素 | {1, 2, 3} 中必须都是同一种类型 |
列表初始化优先匹配 initializer_list 构造函数 | 如果存在多个构造函数,花括号会优先调用这个 |
为了更强大,一般会重载多个构造函数:
MyArray(std::size_t size); // 有参构造
MyArray(); // 默认构造
MyArray(std::initializer_list<T> list); // 列表初始化
这样你还可以支持:
MyArray<int> a1; // 默认构造
MyArray<int> a2(10); // 分配10个元素
MyArray<int> a3{1, 2, 3, 4}; // 列表初始化
4. 拷贝列表初始化
int a = {10};
std::string s = {"abc"};
使用
=
+{}
的组合本质与列表初始化类似,禁止窄化
5. 值初始化
int a{}; // a 为 0
std::string s{}; // s 是空字符串
默认初始化为“零值”
不再有“随机值”问题
6. 默认初始化
int a; // ❌ 值未定义,可能是随机数
局部变量如果未初始化,其值是“野值”
对象成员变量如果没初始化,可能会调用默认构造函数(如果存在)
数组、结构体、指针的初始化方式
数组初始化
int arr[3] = {1, 2}; // arr[2] 自动初始化为 0
int arr[3]{}; // 所有元素初始化为 0
指针初始化
int* p1 = nullptr; // 推荐写法(C++11)
int* p2 = 0; // 旧写法(仍合法,但不推荐)
结构体初始化
struct Point { int x, y; };
Point p1 = {1, 2}; // C++98 风格
Point p2{3, 4}; // C++11 风格,推荐
类初始化与构造函数调用的关系
class Person {
public:explicit Person(int age) { ... }
};Person p1 = 18; // ❌ 错误:explicit 构造函数不能用拷贝初始化
Person p2(18); // ✅ 直接初始化
Person p3{18}; // ✅ 列表初始化(推荐)
explicit
构造函数:禁止隐式转换,因此无法使用 =
赋值初始化,只能用括号或花括号