基于当前 C++11 的广泛应用,这里优先介绍几个 C++ 内容,方便后续讲解类和对象及 STL 库🙌
随着后续 C++ 语法的越来越深入,类型的长度可能会越来越长,因为在一些情境下是不允许把全部命名空间全部打开,所以在写类型时可能会遇到以下问题:
• 类型难于拼写 • 含义不明确导致容易出错
⌨️比如后续学到迭代器有这么个类型
std::map<std::string, std::string>::iterator
或许有聪明的人会想到直接用 typedef 给类型取别名不就好了,使用typedef给类型取别名确实可以简化代码,但是 typedef 有会遇到新的难题:
在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然而有时候要做到这点并非那么容易,而且 typedef 只能定死变量的别名,auto 是自动推断的,因此 C++11 给 auto 赋予了新的含义
早期的 auto:使用auto修饰的变量,是具有自动存储器的局部变量 C++11 的新 auto:根据该变量初始化表达式的类型来自动确定变量自身的类型
🔥值得注意的是:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得
int TestAuto()
{
return10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = TestAuto();
cout<< typeid(b).name() <<endl;
cout<< typeid(c).name() <<endl;
cout<< typeid(d).name() <<endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}
这里 typeid().name() 用于输出类型,后续会学到。分别输出 int、char、int
🔥值得注意的是:使用 auto 定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导 auto 的实际类型
因此 auto 并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型
🚩auto 与指针和引用结合起来使用
int main()
{
int x = 10;
auto a = &x;
auto* b = &x;
auto& c = x;
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
*a = 20;
*b = 30;
c = 40;
return 0;
}
用 auto 声明指针类型时,用 auto 和 auto* 没有任何区别,但用 auto 声明引用类型时则必须加&
🚩在同一行定义多个变量
void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译 器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
🚩auto 不能作为函数的参数
void TestAuto(auto a)
{}
此处代码编译失败,auto 不能作为形参类型,因为编译器无法对 a 的实际类型进行推导
🚩auto 不能直接用来声明数组
void TestAuto()
{
int a[] = {1,2,3};
auto b[] = {4,5,6};
}
数组在声明时,其类型不仅仅取决于数组元素的类型,还与数组的大小有关,使用auto无法准确传达出这个数组大小的信息
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
array[i] *= 2;
for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
cout << *p << endl;
}
但是这种遍历方式写起来有点复杂,过于麻烦,于是在 C++11 开发了基于 for 循环的语法糖,什么是语法糖?语法糖是编程语言中的一个术语,它指的是在编程语言中添加的某种语法,这种语法对语言的功能没有实质性的改变,但是可以让代码更加简洁、易读,更符合程序员的编程习惯
💻基于范围的 for 循环遍历数组:
void TestFor()
{
int array[] = { 1, 2, 3, 4, 5 };
for(auto& e : array)
e *= 2;
for(auto e : array)
cout << e << " ";
return 0;
}
解读一下这个代码,就是依次取数组中的数据赋给 e,这样的代码简洁易读
🔥值得注意的是:该语法糖自动迭代,自动结束;适用于所有数组,后面的容器也会用到;与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环;数组作为形参时提供的是地址,不能用语法糖
在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针,如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:
void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}
NULL 实际是一个宏,在传统的 C **头文件(stddef.h)**中
⌨️NULL可能被定义为字面常量 0,或者被定义为无类型指针 void* 的常量,不论采取何 种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:
void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
程序本意是想通过 f(NULL) 调用指针版本的 f(int*) 函数,但是由于 NULL 被定义成 0,变成调用 f(int),所以为了避免这种情况,创建了一个指针 nullptr 专门代指空指针,之前的 NULL更多当作 0 使用
🔥值得注意的是:
最近天气好冷,大家注意多穿衣服喝热水保暖🥶