## 一.默认初始化
1. **概念** 定义变量时没有指定初始值时进行的初始化操作。
+ 没有(括号)初始化器或等号。
2. 初始化结果
+ 内置变量的默认初始化的值取决于其作用域。
+ 数组的每个元素同样执行默认初始化。
+ 类类型的变量的默认初始化会调用无参构造函数。如果用户未指定,则非静态数据成员同样执行默认初始化。
```c++
//执行了默认初始化
int i;
//其中分配到的空间执行了默认初始化
int *p = new int;
```
> [03-] `new 类型()` 也是默认初始化
## \[03+\][$]二.值初始化
1. **概念** 使用了括号进行初始化,但没有提供初始值的情况。
+ 有括号(初始化器)
+ 没有初始值
2. 初始化结果
+ 内置类型值初始化为0。
+ 数组的每个元素同样执行值初始化。
+ 类类型值初始化会调用相应无参构造函数。如果用户未指定,则非静态数据成员执行默认初始化。
+ 定义标准库容器对象时,若只声明了容器大小,则其元素执行值初始化。
```c++
//其中int() 产生的临时变量执行了值初始化
int i = int();
//其中new int() 分配到的空间执行了值初始化
int *p = new int();
//对,这也是值初始化
vector<int> v(10);
```
>[warning] 若初始化器没有参数,则编译器会将该表达式视为函数声明。
```c++
//这是一个函数声明,而不是一个变量的定义
int i();
```
## 三.直接初始化
1. **概念** 使用括号进行初始化且指定了初始值。
+ 有括号(初始化器)
+ 有初始值
```c++
//i1是直接初始化
int i1(10);
//[11+]i2也是直接初始化
int i2{10};
//v不是直接初始化,而是值初始化
vector<int> v(10);
```
## 四.拷贝初始化
1. **时机**
> 成员初始化列表不支持拷贝初始化。
- 使用等号进行初始化
- 使用一个对象初始化另一个对象
- 从函数返回一个对象
2. 结果
- 若使用一个同类型左值初始化对象,则调用 **拷贝构造函数**(内置类型是直接复制)。
- **\[17+\][17-的编译器可能支持]** 若使用同类型的右值(新标准为纯右值)初始化一个对象时,会发生 **复制消除** ,此时不会创建临时变量,而是直接用右边的表达式初始化左边的对象。
- 若等号左右两边类型不匹配,则调用 **转换构造函数**(内置类型直接隐式转换)。
>[warning] 以下示例请使用支持 C++17标准的编译器运行。
```c++
#include <iostream>
using namespace std;
class Complex
{
private:
double real_;
double imaginary_;
public:
Complex() : real_(0), imaginary_(0)
{
cout << "已调用无参构造函数" << endl;
}
Complex(double real) : real_(real), imaginary_(0)
{
cout << "已调用普通构造函数" << endl;
}
Complex(double real, double imaginary) : real_(real), imaginary_(imaginary)
{
cout << "已调用普通构造函数" << endl;
}
Complex(const Complex &old)
{
this->real_ = old.real_;
this->imaginary_ = old.imaginary_;
cout << "已调用拷贝构造函数" << endl;
}
Complex &operator=(const Complex &old)
{
this->real_ = old.real_;
this->imaginary_ = old.imaginary_;
cout << "已调用拷贝赋值函数" << endl;
return *this;
}
Complex &operator=(const Complex &&old)
{
this->real_ = old.real_;
this->imaginary_ = old.imaginary_;
cout << "已调用移动赋值函数" << endl;
return *this;
}
double get_real()
{
return this->real_;
}
double get_imaginary()
{
return this->imaginary_;
}
Complex &set_real(double new_val)
{
this->real_ = new_val;
return *this;
}
Complex &set_imaginary(double new_val)
{
this->imaginary_ = new_val;
return *this;
}
};
int main()
{
Complex a; //默认初始化,调用无参构造函数
Complex b(10, 10); //直接初始化,调用普通构造函数
Complex c = 10; //拷贝初始化,调用普通构造函数
Complex d = c; //拷贝初始化,调用拷贝构造函数
Complex e(a); //拷贝初始化,调用拷贝构造函数
Complex f = Complex(Complex(1, 2)); //拷贝初始化,且只初始化一次(发生了复制消除),相当于 `Complex f(1,2);`,调用普通构造函数
f = e; //拷贝赋值,调用拷贝赋值函数
f = Complex(3, 4); //移动赋值,先调用普通构造函数,再调用移动赋值函数
return 0;
}
```
>[test]
>已调用无参构造函数
>已调用普通构造函数
>已调用普通构造函数
>已调用拷贝构造函数
>已调用拷贝构造函数
>已调用普通构造函数
>已调用拷贝赋值函数
>已调用普通构造函数
>已调用移动赋值函数
>[warning] 以下示例可使用任意标准的编译器运行。
```c++
#include <iostream>
using namespace std;
class Complex
{
private:
double real_;
double imaginary_;
public:
Complex() : real_(0), imaginary_(0)
{
cout << "已调用无参构造函数" << endl;
}
Complex(double real) : real_(real), imaginary_(0)
{
cout << "已调用普通构造函数" << endl;
}
Complex(double real, double imaginary) : real_(real), imaginary_(imaginary)
{
cout << "已调用普通构造函数" << endl;
}
Complex(const Complex &old)
{
this->real_ = old.real_;
this->imaginary_ = old.imaginary_;
cout << "已调用拷贝构造函数" << endl;
}
Complex &operator=(const Complex &old)
{
this->real_ = old.real_;
this->imaginary_ = old.imaginary_;
cout << "已调用拷贝赋值函数" << endl;
return *this;
}
double get_real()
{
return this->real_;
}
double get_imaginary()
{
return this->imaginary_;
}
Complex &set_real(double new_val)
{
this->real_ = new_val;
return *this;
}
Complex &set_imaginary(double new_val)
{
this->imaginary_ = new_val;
return *this;
}
};
int main()
{
Complex a; //默认初始化,调用无参构造函数
Complex b(10, 10); //直接初始化,调用普通构造函数
Complex c = 10; //拷贝初始化,调用普通构造函数
Complex d = c; //拷贝初始化,调用拷贝构造函数
Complex e(a); //拷贝初始化,调用拷贝构造函数
Complex f = Complex(1, 2); //拷贝初始化,可能先调用普通构造函数,再调用拷贝构造函数,也可能直接调用普通构造函数
f = e; //拷贝赋值,调用拷贝赋值函数
f = Complex(3, 4); //拷贝赋值,先调用普通构造函数,再调用拷贝赋值函数
return 0;
}
```
>[test-gpp]
> 已调用无参构造函数
> 已调用普通构造函数
> 已调用普通构造函数
> 已调用拷贝构造函数
> 已调用拷贝构造函数
> 已调用普通构造函数
> 已调用拷贝构造函数
> 已调用拷贝赋值函数
> 已调用普通构造函数
> 已调用拷贝赋值函数
>[test-vc]
> 已调用无参构造函数
> 已调用普通构造函数
> 已调用普通构造函数
> 已调用拷贝构造函数
> 已调用拷贝构造函数
> 已调用普通构造函数
> 已调用拷贝赋值函数
> 已调用普通构造函数
> 已调用拷贝赋值函数
## \[11+\][$]五.列表初始化
1. **概念** 用大括号进行初始化的情况。如数组和结构体的初始化。
2. 当类没有用户定义的构造函数,或某个构造函数参数类型为 `std::initializer_list` 时,该类可执行列表初始化。
```c++
//v1直接列表初始化
std::vector v1{1,2,3,4,5};
//v2复制列表初始化
std::vector v2 = {1,2,3,4,5};
```
- 阅读说明
- 1.1 概述
- C++基础
- 1.2 变量与常量
- 1.2.1 变量
- 1.2.2 字面值常量
- 字符型常量
- 数值型常量
- 1.2.3 cv限定符
- 1.3 作用域
- 1.3.1 标识符
- 1.3.2 *命名空间
- 1.3.3 作用域
- 1.3.4 可见性
- 1.4 数据类型
- 1.4.1 概述
- 1.4.2 处理类型
- 类型别名
- * auto说明符
- * decltype说明符
- 1.4.3 数组
- 1.4.4 指针
- 1.4.5 引用
- 1.5 表达式
- 1.5.1 概述
- 1.5.2 值的类别
- 1.5.3 *初始化
- 1.5.4 运算符
- 算术运算符
- 逻辑和关系运算符
- 赋值运算符
- 递增递减运算符
- 成员访问运算符
- 位运算符
- 其他运算符
- 1.5.5 *常量表达式
- 1.5.6 类型转换
- 第2章 面向过程编程
- 2.1 流程语句
- 2.1.1 条件语句
- 2.1.2 循环语句
- 2.1.3 跳转语句
- 2.1.4 *异常处理
- 2.2 函数
- 2.2.1 概述
- 2.2.2 函数参数
- 2.2.3 内置函数
- 2.2.4 函数重载
- 2.2.5 * 匿名函数
- 2.3 存储和生命期
- 2.3.1 生命周期与存储区域
- 2.3.2 动态内存
- 2.4 *预处理命令
- 第3章 面向对象编程
- 3.1 概述
- 3.2 类和对象
- 3.3 成员
- 3.3.1 访问限制
- 3.3.2 常成员
- 3.3.3 静态成员
- 3.3.4 成员指针
- 3.3.5 this指针
- 3.4 特殊的成员函数
- 3.4.1 概述
- 3.4.2 构造函数
- 3.4.3 析构函数
- 3.4.4 拷贝语义
- 3.4.5 * 移动语义
- 3.5 友元
- 3.6 运算符重载与类型转换
- 3.6.1 概述
- 3.6.2 重载方法
- 3.6.3 类型转换
- 3.7 继承与多态性
- 3.7.1 概述
- 3.7.2 派生类
- 3.7.3 子类型
- 3.7.4 虚基类
- 3.7.5 虚函数
- 3.7.6 抽象类
- 3.8 模板与泛型
- 3.8.1 概述
- 3.8.2 模板类型
- 3.8.3 *模板参数
- 3.8.4 *模板编译
- 3.8.5 *模板推断
- 3.8.6 *实例化与特例化
- 第4章 C++标准库
- 4.1 概述
- 4.2 输入输出流
- 4.2.1 概述
- 4.2.2 *流的状态
- 4.2.3 *常用流
- 4.2.4 *格式化I/O
- 4.2.5 *低级I/O
- 4.2.6 *随机访问
- 4.3 *C输入输出
- 4.3.1 *字符输入输出
- 4.3.2 *格式化输入输出
- 4.4 * 容器
- 4.4.1 * 概述
- 4.4.2 * 基本操作
- 4.4.3 * 顺序容器
- 4.4.4 * 迭代器
- 4.4.5 * 容器适配器
- 4.5 * 泛型算法
- 4.6 * 内存管理
- 4.6.1 * 自动指针
- 4.7 * 其他设施