目录
auto类型变量(C++11标准支持)
auto关键字介绍
auto关键字的使用
auto关键字基本使用
auto关键字配合指针和引用
auto关键字不可以推导的场景
基于范围的for循环(C++11标准支持)
基于范围的for循环基础使用
基于范围的for循环使用条件
空指针值(C++11标准支持)
声明:本篇时C语言向C++过渡的基础知识的最后一篇
auto类型变量(C++11标准支持)
auto关键字介绍
随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:
- 类型难于拼写
- 含义不明确导致容易出错
例如下面的代码中
#include <string>
#include <map>
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;
}
可以用typedef
来处理上面的问题,但是typedef
也有别的问题
#include <string>
#include <map>
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 char* pstring;
int main()
{
const pstring p1; // 编译失败,展开后为char* const p1,作为const修饰的变量为常量,只有一次赋值的机会,故需要初始化
const pstring* p2; // 编译通过
return 0;
}
auto关键字的使用
auto关键字基本使用
在早期C/C++中auto
的含义是:使用auto
修饰的变量,是具有自动存储器的局部变量
在C++11标准中,标准委员会赋予了auto
全新的含义即:auto
不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto
声明的变量必须由编译器在编译时期推导而得
使用auto
关键字创建变量的语法如下:
auto 变量名;
auto
创建的变量有自动推导类型的特点,例如下面的代码中
//auto关键字
#include <iostream>
using namespace std;
int main()
{
int a = 0;
auto b = a;//b自动推导为int类型
double c = 0.0;
auto d = c;//c自动推导为double类型
cout << "a变量的类型为:" << typeid(a).name() << endl;
cout << "b变量的类型为:" << typeid(b).name() << endl;
cout << "c变量的类型为:" << typeid(c).name() << endl;
cout << "d变量的类型为:" << typeid(d).name() << endl;
return 0;
}
输出结果:
a变量的类型为:int
b变量的类型为:int
c变量的类型为:double
d变量的类型为:double
在上面的代码中,创建了两个普通对象a
和c
,使用auto
关键字创建了两个对象b
和d
,分别自动匹配了a
和c
的类型为int
和double
,使用typeid.name()
查看变量类型
需要注意的是,使用auto
创建的变量一定要初始化,否则编译器无法推导该变量的类型从而报错
//auto关键字
#include <iostream>
using namespace std;
int main()
{
auto e;// 报错
return 0;
}
报错信息:
“e”: 类型包含“auto”的符号必须具有初始值设定项
所以,auto
并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto
替换为变量实际的类型
使用auto
关键字可以同时创建多个变量,但是必须保证一个auto
关键字创建的多个变量匹配一种类型,不可以auto
匹配多个类型
#include <iostream>
using namespace std;
int main()
{
auto a = 1, b = 2, c = 3, d = 4;//所有变量匹配为int类型
auto f = 2.0, g = 2;//不可以同时匹配多个类型
return 0;
}
报错信息:
在声明符列表中,“auto”必须始终推导为同一类型
auto关键字配合指针和引用
auto
关键字创建的变量也可以自动推导为指针和引用类型,也可以直接将auto
关键字创建的变量指定为指针和引用类型,用auto
声明指针类型时,用auto
和auto*
没有任何区别,但用auto
声明引用类型时则必须加&
如下
//auto关键字配合指针和引用
#include <iostream>
using namespace std;
int main()
{
int a = 0;
int* p = &a;
auto p1 = p;//自动推导为int*类型
auto* p2 = p;//指定为int*类型
//auto* p3 = a;//不可以推导
auto r1 = a;//自动推导为int类型
auto& r2 = a;//指定为引用类型
return 0;
}
编译器指示如下:
auto关键字不可以推导的场景
auto
关键字创建的变量不可以作为函数形参
void test(auto a)
{
}
#include <iostream>
using namespace std;
int main()
{
test(1);
return 0;
}
报错信息:
参数不能为包含“auto”的类型
auto
不能直接用于声明数组
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
auto arr1[] = { 1,2,3,4,5 };//不可以直接用来创建数组
return 0;
}
报错信息:
“auto”类型不能出现在顶级数组类型中
但是可以获取到数组的地址
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
auto arr1 = arr;//可以将已经创建的数组给auto创建的变量,自动匹配为int*
cout << "arr1:" << typeid(arr1).name() << endl;
for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
{
cout << "arr1[i]:" << arr1[i] << endl;
}
return 0;
}
输出结果:
arr1:int * __ptr64
arr1[i]:1
arr1[i]:2
arr1[i]:3
arr1[i]:4
arr1[i]:5
基于范围的for循环(C++11标准支持)
基于范围的for循环基础使用
在C++98中需要按照常规方式进行数组的遍历,例如
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
for (int i = 0; i < sizeof(arr)/sizeof(int); i++)
{
cout << arr[i] << " ";
}
return 0;
}
输出结果:
1 2 3 4 5
但是在C++11中可以按照下面的方式进行遍历
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
for (int num : arr)
{
cout << num << " ";
}
return 0;
}
在上面的for
循环中,创建了一个num
变量,将数组中的元素给num
变量,再打印num
变量
鉴于上面的过程说明,如果想用上面的方式修改数组中的元素则不会成功
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
for (int num : arr)
{
num *= 2;
cout << num << " ";
}
cout << endl;
for (int num : arr)
{
cout << num << " ";
}
return 0;
}
输出结果:
2 4 6 8 10
1 2 3 4 5
此时可以配合引用进行原数组内容的修改
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
for (int& num : arr)
{
num *= 2;
cout << num << " ";
}
cout << endl;
for (int num : arr)
{
cout << num << " ";
}
return 0;
}
输出结果:
2 4 6 8 10
2 4 6 8 10
📌
与普通循环类似,可以用continue
来结束本次循环,也可以用break
来跳出整个循环
基于范围的for
循环也可以使用auto
创建循环变量
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5 };
for (auto num : arr)
{
cout << num << " ";
}
cout << endl;
for (auto& num : arr)
{
num *= 2;
cout << num << " ";
}
return 0;
}
输出结果:
1 2 3 4 5
2 4 6 8 10
基于范围的for循环使用条件
for
循环迭代的范围必须是确定的
#include <iostream>
using namespace std;
void test(int* arr)
{
for (auto num : arr)
{
cout << num << " ";//不可以遍历,因为无法确定范围,数组在传参时只会传递第一个元素的地址
}
}
int main()
{
int arr[] = { 1,2,3,4,5 };
test(arr);
return 0;
}
空指针值(C++11标准支持)
在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,基本都是按照如下方式对其进行初始化:
void TestPtr()
{
int* p1 = NULL;
int* p2 = 0;
// ……
}
NULL
实际是一个宏,在传统的C头文件(stddef.h
)中,可以看到如下代码:
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
在上面的宏定义中,NULL
可能被定义为字面常量0,或者被定义为无类型指针(void*
)的常量,不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如在函数重载中可能会使编译器匹配参数为整型的函数,而不是参数为指针类型的函数:
#include <iostream>
using namespace std;
void test(int a)
{
cout << "匹配int类型形参" << endl;
}
void test(int* a)
{
cout << "匹配int*类型形参" << endl;
}
int main()
{
test(0);
test(NULL);
return 0;
}
输出结果:
匹配int类型形参
匹配int类型形参
此时为了使形参匹配到指针类型,必须将NULL
或者0
强制转换为(void*
)类型
void test(int a)
{
cout << "匹配int类型形参" << endl;
}
void test(void* a)
{
cout << "匹配int*类型形参" << endl;
}
int main()
{
test(0);
test((void*)NULL);
return 0;
}
为了解决上面的问题,C++11标准中提出了nullptr用于指定指针空值
#include <iostream>
using namespace std;
void test(int a)
{
cout << "匹配int类型形参" << endl;
}
void test(int* a)
{
cout << "匹配int*类型形参" << endl;
}
int main()
{
test(0);
//test((void*)NULL);
test(nullptr);
return 0;
}
输出结果:
匹配int类型形参
匹配int*类型形参
在使用nullptr
时注意下面的三点:
- 在使用
nullptr
表示指针空值时,不需要包含头文件,因为nullptr
是C++11作为新关键字引入的。 - 在C++11中,
sizeof(nullptr)
与sizeof((void*)0)
所占的字节数相同。 - 为了提高代码的健壮性,在后续表示指针空值时建议最好使用
nullptr
。
#include <iostream>
using namespace std;
int main()
{
cout << "sizeof(NULL):" << sizeof(NULL) << endl;
cout << "sizeof(nullptr):" << sizeof(nullptr) << endl;
cout << "sizeof((void*)0):" << sizeof((void*)0) << endl;
return 0;
}
输出结果:
sizeof(NULL):4
sizeof(nullptr):8
sizeof((void*)0):8