1. c++11之前的初始化
int a = 5; // 拷贝初始化
int b(5); // 直接初始化
int arr[3] = {1, 2, 3}; // 数组初始化
Object obj1; // 默认构造
Object obj2(obj1); // 拷贝构造
- 语法不统一,混乱
2. 列表初始化
- 统一类型,无论什么类型,都可以用 {} 搞定:
统一风格
int a { 5 }; // 直接初始化
int b = { 5 }; // 拷贝初始化
int arr[3] {1, 2, 3}; // 数组初始化
Object obj1 { }; // 默认初始化
Object obj2 { obj1 }; // 拷贝构造
std::vector<int> v {1, 2, 3}; // 容器初始化
禁止窄化转换
- 传统的初始化允许隐式类型转换,可能导致数据精度丢失
int a = 3.14; // OK,但 a = 3,数据丢失
- 列表初始化会在编译期检查并禁止窄化转换:
int a { 3.14 }; // 错误!窄化转换被禁止
int arr[] {1, 2, 3.3}; // 错误!3.3 会导致窄化
简化容器初始化
- 在C++11之前,初始化一个 vector 需要两步:
int arr[5] = {1, 2, 3, 4, 5};
std::vector<int> v(arr, arr + 5);
- 有了列表初始化,一行搞定:
std::vector<int> v {1, 2, 3, 4, 5};
3. 初始化语法细节
- 直接列表初始化:
T object { arg1, arg2, ... }; // 具名变量
T { arg1, arg2, ... }; // 无名临时量
new T { arg1, arg2, ... }; // 动态内存
return { arg1, arg2, ... }; // 函数返回值
- 复制列表初始化
T object = { arg1, arg2, ... }; // 拷贝形式的列表初始化
function({ arg1, arg2, ... }); // 函数参数
object = { arg1, arg2, ... }; // 赋值
4.聚合体
-
聚合体的定义(c++17及之前)
一个类(或结构体)如果满足以下所有条件,就是聚合类型:
(1)没有用户声明的构造函数(包括默认构造函数)
(2)没有私有或受保护的非静态数据成员(所有非静态成员都是 public)
(3)没有基类(不继承任何类)
(4)没有虚函数
(5)非静态数据成员没有类内初始化器(即不能写成 int x = 5; 或 int x{5};) -
聚合类型
struct Point {
int x;
int y;
}; // 满足所有条件,是聚合体
struct Person {
std::string name;
int age;
}; // 也是聚合体
- 非聚合类型
struct NotAggregate1 {
virtual void f() {} // 有虚函数
};
struct NotAggregate2 {
private:
int x; // 有私有非静态成员
};
struct NotAggregate3 {
NotAggregate3(int) {} // 有用户声明的构造函数
};
struct NotAggregate4 {
int x = 10; // 有类内初始化器
};
5. 聚合体初始化
聚合体最大的特点就是可以用 {} 按成员声明顺序进行初始化
struct Point {
int x;
int y;
};
Point p1 {1, 2}; // p1.x = 1, p1.y = 2
Point p2 = {3, 4}; // 同样有效
6. 非聚合体与 std::initializer_list
对于非聚合类型(比如有自定义构造函数的类),列表初始化会尝试匹配构造函数:
- 匹配普通构造函数
class Foo {
public:
Foo(int a, double b) : x(a), y(b) {}
private:
int x;
double y;
};
Foo f {42, 3.14}; // 调用 Foo(int, double)
- 匹配 std::initializer_list 构造函数
如果想要一个类支持任意长度的列表初始化,可以定义一个接受 std::initializer_list 的构造函数:
#include <initializer_list>
#include <iostream>
class MyVector {
public:
MyVector(std::initializer_list<int> list) {
for (auto it = list.begin(); it != list.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
};
MyVector v1 {1, 2, 3}; // 输出: 1 2 3
MyVector v2 {1, 2, 3, 4, 5}; // 输出: 1 2 3 4 5
- std::initializer_list是一个轻量级代理对象,提供对 const T 类型数组的访问:
它不拥有底层数据,只是指向编译器生成的临时数组
内部通常实现为一对指针(或指针+长度)
复制 initializer_list 不会复制底层数组
元素是只读的(const)
只能存储同类型的元素
// initializer_list 的典型用法
void print(std::initializer_list<int> list) {
for (int x : list) {
std::cout << x << " ";
}
}
print({1, 2, 3, 4}); // 输出: 1 2 3 4
- 当一个类同时有匹配的普通构造函数和 initializer_list 构造函数时,initializer_list 版本优先:
class Foo {
public:
Foo(int a, int b) { std::cout << "普通构造\n"; }
Foo(std::initializer_list<int>) { std::cout << "initializer_list构造\n"; }
};
Foo f1 {1, 2}; // 输出: initializer_list构造(而不是普通构造!)
Foo f2 (1, 2); // 输出: 普通构造(圆括号调用普通构造)
浙公网安备 33010602011771号