👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨
前言
本章是补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的。
目录
- 前言
- 一、auto关键字
- 1.1 问题引入
- 1.2 auto简介
- 1.3 auto的使用细则
- 1.4 常见auto不能推导的场景
- 1.5 基于范围的for循环(重点)
- 1.5.1 遍历数组
- 1.5.2 用auto修改数组元素
- 1.6 范围for的使用条件
- 二、 指针空值nullptr
一、auto关键字
1.1 问题引入
随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:
1. 类型难于拼写(过长)
2. 含义不明确导致容易出错
来看看以下代码:
#include <iostream>
#include <string>
#include <map>
using namespace srd;
int main()
{
std::map<std::string, std::string> m{ { "apple", "苹果" },
{"orange","橙子" },
{"pear","梨"} };
std::map<std::string, std::string>::iterator it = m.begin();
while (it != m.end())
{
//....
}
return 0;
}
以上代码刚刚对于
C++
初学者可能看不懂,而std::map<std::string, std::string>::iterator
是一个类型还是看的出来的吧,但由于该类型太长了,写的也累。所以有些人可能想:可以通过typedef
给这么长的类型取别名
例如:
#include <iostream>
#include <string>
#include <map>
using namespace std;
//重命名长类型
typedef std::map<std::string, std::string> Map;
int main()
{
Map m{ { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };
Map::iterator it = m.begin();
while (it != m.end())
{
//....
}
return 0;
}
使用
typedef
给类型取别名确实可以简化代码,可能会引起以下问题:
- 难以理解:
typedef
可以创建新的类型名称,但可能会使代码难以理解和维护。如果类型名称太过简略或不符合命名规范,会导致代码的可读性降低。- 可能会与其他定义冲突:由于
typedef
允许用户创建新的类型名称,因此可能会与其他定义发生冲突。这可以通过遵循命名规则和避免使用与其他名称相同的名称来避免。- 难以进行调试:由于
typedef
创建了新的类型名称,因此调试时可能会难以确定变量的类型。这可以通过良好的代码注释和为类型名称选择有意义的名称来避免。- 可能会导致错误:
typedef
也可以用于创建指向其他类型的指针或引用类型。如果不小心使用了错误的类型名称,可能会导致程序错误。这可以通过在代码中进行仔细的检查和测试来避免。
因此,C++11给auto赋予了新的含义。
1.2 auto简介
在早期auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的是它使用的一点也不频繁。因此,C++11中,标准委员会赋予了auto全新的含义:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,它可以通过赋值操作符右边的表达式来推导出其类型。
typeid
可以打印变量类型
1.3 auto的使用细则
1. auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别
但用auto声明引用类型时则必须加&
2. 在同一行定义多个变量
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错
第
96
行代码会编译失败,因为c和d的初始化表达式类型不同。
1.4 常见auto不能推导的场景
1. auto不能作为函数的参数
此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
2. auto不能直接用来声明数组
1.5 基于范围的for循环(重点)
1.5.1 遍历数组
- 普通方法遍历数组
对于普通的遍历,我们需要先求出数组大小。而对于
auto
遍历,却缺少了这一步,让我们继续往下看
- auto遍历数组
for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。它本质的意思是:依次取数组
arr
中的元素赋值给e
1.5.2 用auto修改数组元素
对于auto修改数组元素,要运用到引用,相当于对
arr
取别名
1.6 范围for的使用条件
1. for
循环迭代的范围必须是确定的
2. 迭代的对象要实现++和==的操作
二、 指针空值nullptr
在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:
#include <iostream>
#include <stddef.h>
using namespace std;
int main()
{
int* p = NULL;
int* pp = 0;
return 0;
}
NULL
实际是一个宏,在传统的C头文件(stddef.h)中,我们通过查询cplusplus网站来查看其属性
可以看到,
NULL
可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:
- 程序本意是想通过
NULL
调用指针版本的f(int*)
函数,但是由于NULL
被定义成0
,因此与程序的初衷相悖。- 这是因为在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转
(void*)NULL
。
但C++11
引入了nullptr
,因此可以不用强转NULL
但需要注意的是:
- 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
- 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
- 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。