C++ 各种对象的初始化
前言
C++的各种类型的初始化主要在默认初始化
以及初始化格式
上有区别。初始化
是指一个对象在创建时获得了一个特定的值。
一、初始化格式
1.算术类型的初始化
// 所有的算术类型都可以采用以下四种方式进行初始化
int a_int = 1;
int a_int = { 1 };
int a_int(1);
int a_int{ 1 };
// 通过花括号初始化的形式被称为列表初始化,以此方式初始化时若初始值存在丢失信息的风险,编译器将报错。
int a_int{ 1.5 }; // 错误:1.5会被丢失小数信息
int a_int{ 10000000000000000000 }; // 错误:初始化的值过大超出int类型容量
unsigned int a_uint{ -1 }; // 错误:使用负值初始化无符号数
long b_int{ 1 }; // 正确
long b_int{ 1.6 }; // 错误:依然是1.6会被丢失小数信息
double c_double{ 1.8 }; // 正确
bool d_bool{ 6 }; // 错误:bool类型实际值只有0和1
char e_char{ '3' }; // 正确
2.复合类型的初始化
2.1 引用的初始化
定义引用时,程序把引用和初始值绑定
在一起。引用并非一个对象,只是为绑定对象起的另一个名字。
int i = 42;
int& a = i; // 正确:通过修改a修改i的值
int& b; // 错误:引用将和它的初始值一直绑定在一起,因此引用必须初始化
double& c = i; // 错误:引用的类型需要与绑定对象一致
2.2 指针的初始化
指针相对于引用来说,也实现了对其他对象的间接访问。
不同点在于,其一,指针本身是一个对象,可以赋值和拷贝,也可以在生命周期中先后指向几个不同的对象。
其二,指针无须在定义时赋初值。
指针在初始化时经常要结合取地址符(操作符&)
以及解引用符(操作符*)
。因为指针的值是一个对象的地址,通过取地址符对目标对象取地址给指针进行赋值。当需要使用指针所指对象的值时,可以通过解引用符来进行获取。
int i = 42;
int* p = &i; // 正确:p的值为i的地址
cout << p << endl; // 输出的是i的地址
cout << *p << endl; // *p输出的是i的值
double* q = &i; // 错误:指针初始化中,其类型必须与目标对象相同。
int* p1 = nullptr // 正确:指针初始化为空指针
2.3 数组的初始化
数组由数组名字以及其维度组成,维度必须大于0,编译的时候维度应该是已知的,也就是说维度必须是一个常量表达式。
// 维度必须是常量
int arr[10]; // 正确:直接进行初始化,但是其中的值是无法预测的
int a = 42;
const int b = 42;
int arr_1[a]; // 错误:维度必须是常量
int arr_2[b]; // 正确:b是一个常量
int arr_3[get_size()]; // 当get_size()为一个常量表达式时正确
// 数组中的元素进行默认初始化
int b[3] = { 0,1,2 };
int b_1[] = { 0,1,2 }; // 维度自动定义为3
int b_2[5] = { 0,1,2 }; // 此时数组中的值为0,1,2,0,0
// 字符串常量在其末尾处有一个空字符
char a1[] = { 'a','b','c' }; //值为'a','b','c'
char a2[] = "abc"; //值为'a','b','c','\0'
string temp = "abc";
const char *a3 = temp.c_str(); // C++的字符串string赋值给char数组
const char *a3 = { temp.c_str() }; // 同上效果
char n[20];
string s = "hello,world!";
strcpy(n, s.c_str()); // 正确,但是不安全显示过时,需加入对应包以及解除警告,#include <cstring>以及#pragma warning (disable:4996)
// 数组不能拷贝赋值
int c[] = { 0,1,2 };
int c1[] = a; // 错误:数组不能拷贝赋值
c1 = c; // 错误:数组不能拷贝赋值
// 复杂的数组声明
int x[10];
int* d[10]; // 正确:含10个整数指针的数组
int& d1[10] = x; // 错误:无法定义引用的数组,引用不是对象
int(*d2)[10] = &x; // 正确:d2是一个指针,指向一个含10个整数的数组
int(&d3)[10] = x; // 正确:d3是一个引用,引用一个含10个整数的数组
3 标准库类型string的初始化
string s1; // 初始化值为空字符串
string s2(s1); // 拷贝
string s3("value"); // 初始化为字面值"value",不含最后的空字符
string s5 = "value"; // 同上
int n = 9;
string s4(n, 'c'); // 初始化为n个'c'连着的
4 标准库类型vector的初始化
vector<int> v1; // 默认初始化,其中不含任何元素
vector<int> v2(v1); // 用v1拷贝给v2
vector<int> v3 = v1; // 用v3拷贝给v1
// 以下四种初始化方式,n未声明则为整数,当n可以表示列表的初始值,也可以表示元素的数量,一般情况下可以用花括号或圆括号进行区分。
vector<int> v4(n); // 有n个元素,每个元素都是一个T
vector<string> v4(n); // n="w",n的值不是整数时,默认切换为花括号的形式,有一个元素,为n
vector<int> v4{n}; // 只有1个元素,值为T的初始值
vector<int> v4(n, val); // 有n个元素,每个都为val
vector<string> v4(n, val); // n="2",当n的值不是整数时,默认切换为花括号的形式,有两个元素,分别为n和val
vector<int> v4{n, val}; // 有两个元素,分别是n或val
vector<int> v6 = { a,b,c }; // 列表的初始化形式
二、默认初始化
默认初始化主要区别在于是否在函数体内部,一般在函数体外会进行默认初始化,函数体内大多不会进行初始化。
int a; // 初始化为0
float b; // 0
double c; // 0
bool d; // 0
char e; // ''
// string类型不管在函数体外还是体内都进行默认初始化
string f; // 空字符串""
int g[3]; // g中元素全初始化为0
// vector不管在函数体外还是体内都进行默认初始化
vector<int> h(10); // h中元素全初始化为0
vector<string> i(10); // i中元素全初始化为空字符串""
// 自定义数据结构也是在函数体外进行默认初始化,函数体外不会进行初始化
struct mine {
int b;
};
mine j; // j中成员b有初始值0
int main()
{
int a1; // 无初始值
float b1; // 无
double c1; // 无
bool d1; // 无
char e1; // 无
string f1; // 空字符串""
int g1[3]; // 数组内元素都是一些无法预测的值
vector<int> h1(10); // 其中元素全为0
vector<string> i1(10); // 其中元素全为空字符串""
mine j1; // j1中成员b中无初始值
}
当使用没有初始值的对象时将发生错误。