学习内容
本节学习花括号 或等号 初始化器在各版本中的异同
初始化器用法:
1、声明时初始化,直接在变量定义时赋值
cpp
int age = 11;
string name = "Alice";
int t1(10);
string t2("Hello");
std::vector<int> v(5,0); // 5个0
int x = {};
string flag = {};
int arr[5] = {};
int x1{};
string x2{};
int y1{20};
int arr[]{1,2,3};
std::vector<int> vec{1,2,3};
struct Point p{1,2};
int y = {10};
int arrs[3] = {1,2,3};
std::vector<int> vec = {1 ,2 ,3 };
struct Point{ int x; int y};
Point p = { 100 ,20 }; //聚合类型
Point p = {.y = 50 , .x = 10 }; //按照成员名指定,不用关心顺序 C++20
int arr[5] = {[2] = 20 , [3] = 30 }; //按照索引指定值,C++20
2、new表达式初始化对象/数组
cpp
class Person
{
public:
int age ;
string name;
Person(int a, string n) : age(a) , name(n){}
}
Person* p = new Person(12,"Bob");
cout << p->age << p->name << endl;
//防止泄漏
if (p) delete p;
p = nullptr;
int *arr = new int[5]{1,2,3,4,5}; //初始化元素
delete [] arr; //必须加[],否则只释放首地址
arr = nullptr;
3、构造函数初始化
cpp
class Person
{
public:
int age ;
string name;
Person() = default;
Person(int a, string n) : age(a) , name(n){}
}
Person pp; //无参构造
Person p(12,"Bob"); //有参构造
4、函数参数初始化
cpp
void Print(int num , string name)
{
cout << num << name << endl;
}
Print(101 , "Ana"); //实参调用形参
5、函数返回值初始化
cpp
int add(int a , int b)
{
return a+b;
}
int result = add(3,5);
6、隐式默认初始化,全局变量、类的成员变量(未显式初始化)、静态变量,编译器会自动赋予默认初始化器;局部变量(函数内)需显式赋值才能使用
cpp
//全局变量
int g_age;
string g_name;
class A {
public:
int ages;
string names;
}
int main()
{
//直接输出全局变量,未赋值情况
cout << g_age << g_name<<endl;
//类成员初始化
A a;
cout << a.ages << a.names << endl;
}
7、数组初始化,静态数组、栈内存
cpp
int arr1[5] = { 1 ,2 ,3 ,4 ,5 };
int arr2[] = { 1 , 2 , 3 , 4 , 5 };
int arr3[4] = { 1, 2 }; //其他元素默认为0
注:
- 如果未为对象指定初始化器,那么对象是默认初始化;如果未为引用指定初始化器,编译器报错
- 如果为对象使用()初始化,那么对象是值初始化;如果为引用指定(),则编译器报错
非局部变量
包含以下类型:
- 全局变量(整个程序可见)
- 静态局部变量(函数内部static声明,运行周期多次调用)
- 类的静态成员变量
它们都有静态存储器(程序从启动到结束都存在):静态初始化(编译/加载) -> 动态初始化(运行阶段,main之前)
1、静态初始化: 在程序加载时完成,速度快且顺序确定
- 常量初始化:编译器常量constexpr或字面量,加载时就确定了值
cpp
const int a = 10;
static double pai = 3.14;
constexpr int b = 20;
- 零初始化器:没有显式初始化值,变量被置为0 / nullptr / false
cpp
int x;
static int y;
char* p;
2、动态初始化: 静态初始化完成后,在main函数执行前,用来处理无法再编译期确定值的初始化(调用函数、依赖其他变量)
- 无序动态初始化:初始化顺序不确定,例如模版的静态数据成员、变量模板
cpp
template < class T>
struct Test{
static T data; }; //静态模版成员 ,无序动态初始化
template <class T> T Test<T>::data = T{};
- 部分有序动态初始化:在同个cpp内,若a先于b之前定义并初始化(需要顺序初始化);跨cpp文件仍为无序
cpp
inline int a = 1;
inline int b = a; //a先初始化,然后用a初始化b
- 有序动态初始化:在同个cpp内,按源代码定义顺序初始化;跨cpp文件顺序完全不确定
3、早期动态初始化
编译器优化方法:把某些本应动态初始化的变量,提前到编译期当成静态初始化处理
需满足以下条件:
- 初始化过程不会修改其他对象
- 最终结果和不做优化前的值完全一样
4、延迟动态初始化(优化启动速度,避免不必要的初始化开销)
- 只要在cpp文件用到此变量,那么它一定会在用之前就完成初始化,没被用到的永远不会初始化
- 只要有一个被用到且有作用(构造函数里有I/O、修改全局状态、分配资源),同cpp所有这类变量都要初始化
- 内联变量的动态初始化如果被延迟,保证它在第一次被代码使用前完成初始化
cpp
int & GetValue()
{
static int x = 10; //用到该函数时,才会静态初始化
return x;
}
链接器与初始化器限制
- 若变量有外部/内部链接,在块作用域里声明时不能写初始化器,只能用extern声明,不能同时定义
- 静态成员数据必须在类外定义并初始化(C++17开始可在类内初始化)
cpp
class A
{
public:
static int data ;
inline static int element = 19; //C++17开始支持类内初始化
}
int A::data = 14; //类外定义+初始化
- 非局部变量的析构顺序:遵循初始化的逆序,对于静态局部变量会在程序结束时按照逆序析构,调用std::exit()会触发所有非局部变量的析构,abort()则跳过析构
详情直接点链接查看初始化列表(std::initializer_list)