C++命运石之门代码抉择:C++入门(下)

举报
澪贰 发表于 2025/10/28 14:22:03 2025/10/28
【摘要】 基于当前 C++11 的广泛应用,这里优先介绍几个 C++ 内容,方便后续讲解类和对象及 STL 库🙌

4.C语言过渡到C++(下)

4.1 auto 关键字(C++11)

4.1.1 为什么要用 auto?

随着后续 C++ 语法的越来越深入,类型的长度可能会越来越长,因为在一些情境下是不允许把全部命名空间全部打开,所以在写类型时可能会遇到以下问题:

类型难于拼写
含义不明确导致容易出错

⌨️比如后续学到迭代器有这么个类型

std::map<std::string, std::string>::iterator

或许有聪明的人会想到直接用 typedef 给类型取别名不就好了,使用typedef给类型取别名确实可以简化代码,但是 typedef 有会遇到新的难题:

在编程时,常常需要把表达式的值赋值给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然而有时候要做到这点并非那么容易,而且 typedef 只能定死变量的别名,auto 是自动推断的,因此 C++11 给 auto 赋予了新的含义

4.1.2 什么是 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替换为变量实际的类型

4.1.3 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[] = {456};
}

数组在声明时,其类型不仅仅取决于数组元素的类型,还与数组的大小有关,使用auto无法准确传达出这个数组大小的信息

4.2 基于范围的 for 循环(C++11)

💻遍历数组的普通方法

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来跳出整个循环;数组作为形参时提供的是地址,不能用语法糖

4.3 指针空值 nullptr (C++11)

在良好的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 使用

🔥值得注意的是

  1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr 是 C++11 作为新关键字引入的
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0) 所占的字节数相同
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr

最近天气好冷,大家注意多穿衣服喝热水保暖🥶

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。