C++(一)----C++基础

news2024/9/17 8:33:02

1.C++的发展史

C语言诞生后,很快普及使用,但是随着编程规模增大且越来越复杂,并且需要高度的抽象和建模时,C语言的诸多短板便表现了出来,为了解决软件危机,上世纪八十年代,计算机界提出了oop(object
oriented programming:面向对象编程)思想,支持面向对象的程序设计语言应运而生。

1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

1979年,贝尔实验室的本贾尼等人试图分析unix内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为C with classes

语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。以下是C++的历史版本

C with classes
类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等
C++1.0 添加虚函数概念,函数和运算符重载,引用、常量等

C++2.0 更加完善支持面向对象,新增保护成员、多重继承、对象的初始化、抽象类、静态成员以
及const成员函数

C++3.0 进一步完善,引入模板,解决多重继承产生的二义性问题和相应构造和析构的处理

C++98 C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美国标准化
协会认可,以模板方式重写C++标准库,引入了STL(标准模板库)

C++03 C++标准第二个版本,语言特性无大改变,主要:修订错误、减少多异性

C++05 C++标准委员会发布了一份计数报告(Technical Report,TR1),正式更名C++0x,即:计
划在本世纪第一个10年的某个时间发布

C++11 增加了许多特性,使得C++更像一种新语言,比如:正则表达式、基于范围for循环、auto
关键字、新容器、列表初始化、标准线程库等

C++14 对C++11的扩展,主要是修复C++11中漏洞以及改进,比如:泛型的lambda表达式,
auto的返回值类型推导,二进制字面常量等

C++17 在C++11上做了一些小幅改进,增加了19个新特性,比如:static_assert()的文本信息可
选,Fold表达式用于可变的模板,if和switch语句中的初始化器等

C++20 制定ing

C++23

C++26

2.C++的重要作用

在开发语言排行榜上,C++几乎稳居前三,可见其泛用性。
(下图为23年6月的)

(下图是2024年8月的)

在工作领域,C++在以下领域有其独到的优势:

  1. 操作系统以及大型系统软件开发
  2. 服务器端开发
  3. 人工智能
  4. 网络工具
  5. 游戏开发
  6. 嵌入式领域
  7. 数字图像处理
  8. 分布式应用
  9. 移动设备
    在校招领域 ,不多说直接上图:


    笔试题:网易笔试、迅雷笔试等等
    面试题:

从校招中公司岗位的技能要求,以及学长面经总结了解到,公司在校招期间更看重学生的基础,最主要是:语言(至少掌握一门面向对象语言java/C++)、数据结构、操作系统、网络、数据库、设计模式等,而本门C++的授课内容,更注重学生的实践动手能力、工作中的应用以及笔试面试中的技巧,最后达到能够正常工作以及学习即可。

3.C++的基本语法

闲话少说,接下来直接进入C++学习。
下面是C++的关键字:
asm do if return try continue auto double inline short typedef for bool dynamic_cast int signed typeid public break else long sizeof typename throw case enum mutable static union wchar_t catch explicit namespace static_cast unsigned default char export new struct using friend class extern operator switch virtual register const false private template void true const_cast float protected this volatile while delete goto reinterpret_cast
相比C语言足足多了一倍!(C语言32个,C++63个)
但是这里先不细讲,这些关键字在以后的学习中都会学到的。

3.1 命名空间

什么是命名空间?为什么要有命名空间?

在C/C++中,变量、函数和后面要学到的类都是大量存在的,如果这些变量、函数和类的名称都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
例如:在C语言中

#include<stdio.h>
#include<stdlib.h>
int rand=10;
int main()
{
	printf("%d",rand);
	return 0;
}

上面这个函数会报错:

它会告诉你,rand是一个函数名字,这里的全局变量rand有命名冲突,因为在库中有了rand这个函数,再次使用rand这个名字定义变量或者函数时,编译器会分不清你到底想使用哪个rand。

可能你觉得问题不大,大不了我换个名字就好了嘛,但是在大型工程项目中,数以MB的大小里面的变量名字可不是简简单单换个名字这么简单

C++为了解决这个问题,引出了命名空间这个玩法(C++兼容C语言的所有语法)

命名空间如何定义呢?

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员

namespace N1 // N1为命名空间的名称
{
 // 命名空间中的内容,既可以定义变量,也可以定义函数,也可以定义结构体
 int a;
 int rand;
 int Add(int left, int right)
 {
 return left + right;
 }
 struct student
 {
 	char name[];
 	int age;
 }
}
//2. 命名空间可以嵌套
namespace N2
{
 int a;
 int b;
 int Add(int left, int right)
 {
 return left + right;
 }
 
 namespace N3
 {
 int c;
 int d;
 int Sub(int left, int right)
 {
 return left - right;
 }
 }
}
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
namespace N1
{
 int Mul(int left, int right)
 {
 return left * right;
 }
}

如最上面的N1,在这个代码中,rand就定义在命名空间N1中,这和库函数中定义的全局函数rand()不在同一个空间中,所以可以同时存在

命名空间定义后如何使用呢?

namespace N
{
 int a = 10;
 int b = 20;
 int Add(int left, int right)
 {
 return left + right;
 }
 int Sub(int left, int right)
 {
 return left - right;
 }
}
int main()
{
 printf("%d\n", a); // 该语句编译出错,无法识别a
 return 0;
}

编译这个程序,会发现编译出错,编译器无法识别a,那是因为我们定义了命名空间N之后并没有使用它,命名空间的使用方法有以下三种:
1.加命名空间名称及作用域限定符:

int main()
{
 printf("%d\n", N::a);//::就是作用域限定符
 return 0; 
}

2.使用using将命名空间中的成员引入:(写项目的时候使用)

using N::b;//将N::b引入全局中
int main()
{
 printf("%d\n", N::a);
 printf("%d\n", b);
 return 0; 
}

3.使用using namespace命名空间名称引入(建日常使用)

using namespce N;//这里相当于把N展开了,也就是把N的成员加入到全局中
int main()
{
 printf("%d\n", N::a);
 printf("%d\n", b);
 Add(10, 20);
 return 0; 
}

当然,在命名空间N1中的rand被展开之后相当于加入到全局域,再使用的时候还是会和库里的rand()函数冲突

命名空间的存在奠基了C++能作为创建一个大工程的语言,一个大工程往往会分组安排任务,C++使得每个小组可以使用不同的命名空间,即使命名空间定义相同的名字,编译器也会帮你进行合并,这对于开发者来说十分方便!

以下是一些使用 C++ 开发的知名游戏:

  1. 《英雄联盟》:这是一款非常受欢迎的多人在线战斗竞技游戏。
  2. 《使命召唤》系列:著名的第一人称射击游戏。
  3. 《古墓丽影》系列:动作冒险游戏。
  4. 《星际争霸》系列:经典的即时战略游戏。
    以及 绝地求生****巫师三

3.2 C++的输入输出

讲到现在,我们甚至还不会用C++写一个“hello world”,这怎么行?上代码:

#include<iostream>
using namespace std;
int main()
{
 cout<<"Hello world!!!"<<endl;
 return 0;
}

代码中,cout就是标准输出(控制台),除此之外还有cin——标准输入(即键盘),使用cout标准输出和cin标准输入时,必须包含< iostream >头文件以及std标准命名空间。(endl就是换行)
需要注意的是:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因此推荐使用+std的方式。
估计细心的同学已经发现了,使用C++进行输入输出的时候不需要增加格式控制,也就是C语言格式化输入输出的%d、%c等。直接cin、cout即可。而且可以一个cin(cout)进行连续输入(输出)

#include <iostream>
using namespace std;
int main()
{
 int a;
 double b;
 char c;
 
 cin>>a;
 cin>>b>>c;
 
 cout<<a<<endl;
 cout<<b<<" "<<c<<endl;
 return 0;

}

3.3 缺省参数

缺省,可能单看这个名字看不出来什么意思,但是找到它的英文就知道了:default,其实就是默认。
缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。
缺省参数分为全缺省和半缺省
全缺省是这样的:

void Func(int a = 10, int b = 20, int c = 30)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

Func函数中a,b,c都给了默认值,就叫全缺省
而半缺省长这样:

void Func(int a, int b = 20, int c = 30)
 {
     cout<<"a = "<<a<<endl;
     cout<<"b = "<<b<<endl;
     cout<<"c = "<<c<<endl;
 }

这里只有b和c给了缺省值,而a没有,就叫半缺省

需要注意的是,半缺省参数只能从右往左依次给出,不能间隔着给,而且缺省参数不能在函数声明和定义中同时出现

//in a.h
void TestFunc(int a = 10);
// in a.c
void TestFunc(int a = 20)
{}
// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

而且缺省值必须是常量或者全局变量。(C语言不支持这个语法)

3.4 函数重载

从前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前
者是“谁也赢不了!”,后者是“谁也赢不了!”。这个笑话表明,自然语言中同样的句子可能有不同的含义,而函数重载就和这个类似。
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题

int Add(int left, int right)
{
 return left+right;
}
double Add(double left, double right)
{
 return left+right;
}
long Add(long left, long right)
{
 return left+right;
}
int main()
{
 Add(10, 20);
 Add(10.0, 20.0);
 Add(10L, 20L);
 
 return 0;
}

代码中,有三个函数,名字都为Add,但是它们的参数类型不同
函数重载还可以是参数数量不同:

void f()
{
 cout << "f()" << endl;
}
void f(int a)
{
 cout << "f(int a)" << endl;
}

以及参数顺序不同:

void f(int a, char b)
{
 cout << "比较航空航天大学" << endl;
}
void f(char b, int a)
{
 cout << "哈尔滨佛学院" << endl;
}

但是形参名字不行:

short Add(short x,short y)
{
	return x+y;
}
short Add(short y,short x)
{
	return x+y;
}

此外要注意:返回值不同不能构成重载,如:

short Add(short left, short right)
{
 return left+right;
}
int Add(short left, short right)
{
 return left+right;
}

那为什么C语言不支持而C++支持呢?

这就涉及它们整个编译的过程了:在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。在这些过程中,C++文件和C文件的处理不同。

C语言为什么不支持?
首先创建三个文件:func.h func.c main.c 在.h文件中声明两个函数:

int func(int x,int y);
int func(int x,double y);

这三个文件会经历:
1.预处理:头文件展开、宏替换、条件编译以及去掉注释,这个过程结束之后func.h被展开了,main.c func.c成为func.i main.i文件
2.编译:语法检查和生成汇编代码func.i和main.i变成了func.s main.s(文件内容是汇编代码)
3.汇编:将汇编代码转换成二进制码,以便机器能够读懂,此时变成func.o main.o
4.链接(最关键):链接时,.o文件会合并在一起,而且还需找一些只给了声明的函数
的函数地址,而每一个.o文件都有一个符号表,符号表中存放函数的地址,当main文件要调用这个函数时,会去符号表中找函数的地址

而符号表中两个func函数的地址,编译器不知道应该调用哪个,所以c程序不支持函数重载。

那C++为什么支持呢?
相比起C程序而言,C++新增了一个函数名修饰规则来支持函数重载,这个规则就是将函数的参数带入符号表,所以参数的类型,数量,顺序不同,代表的是不同的函数,找地址时就不会出错!
将C++代码转到反汇编,我们可以看到:

函数参数的类型,数量,顺序不同,那么对应在符号表中的名字就不一样,main文件再去找函数地址时就不会冲突。这个命名规则C++标准并没有具体规定,由每个编译器自己决定,如VS2022就与g++不同(VS2022的命名规则比较诡异),但是一定可以保证的是,参数不同的函数名字不同
对比C语言,c程序符号表中只有一个函数名,函数参数没有参与进来,所以C程序不支持相同函数名的函数。

那么这时候就会有同学问了,如果在符号表命名规则中加入返回值不就可以让不同的返回值支持函数重载了吗?
答案是不能!
因为在调用的时候并没有返回值,比如下面这两个函数:

void add(double x, double y)
{
	return ;
}
double add(double x, double y)
{
	return x+y;
}
double a=0.5,b=0.5;

在调用的时候,我们只有add(a,b),那么即使符号表的问题解决了,调用规则摆在这,还是无法确定应该调用符号表中的哪个函数。(除非你把调用规则也改掉,让它在调用的时候带上返回值一起)

有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,他提供tcmallc()和tcfree两个接口来使用,但如果是C项目就没办法使用,那么他就使用extern “C”来解决。

extern "C" int Add(int left, int right);
int main()
{
 Add(1,2);
 return 0;
}

此时就会链接时报错:error LNK2019: 无法解析的外部符号_Add,该符号在函数 _main 中被引用
因为函数按照C语言的规则来命名。

下面两个函数构成函数重载吗?

void TestFunc(int a = 10)
{
 cout<<"void TestFunc(int)"<<endl;
}
void TestFunc(int a)
{
 cout<<"void TestFunc(int)"<<endl;
}

答案是不构成,因为对于TestFunc(int)编译器根本不知道应该调用哪个函数,会直接报错:

3.5 引用

众所周知,指针是C语言的精髓所在,引用之于C++犹如指针之于C,甚至C++不仅支持指针(java、python等语言都不支持指针),而且支持引用,可谓卧龙凤雏齐聚也。

什么是引用?
引用不是新定义一个变量,而是给已存在变量取了一个别名。
比如孙悟空,又叫孙行者,又叫齐天大圣,又叫弼马温,又叫斗战圣佛。
再比如我,除了我的名字,又叫全世界最帅的人(bushi)。
但是编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

int a=10;
int& b=a;
printf("%p\n%p",&a,&b);

引用是怎么使用的?
定义一个引用:
类型& 引用变量名(对象名) = 引用实体;

void TestRef()
{
 int a = 10;
 int& ra = a;//<====定义引用类型
 
 printf("%p\n", &a);
 printf("%p\n", &ra);
}

引用类型需要与引用实体同类

引用有三个特点:
1.必须在定义时初始化
2.一个变量可以有多个引用
3.一个引用一旦有了实体就不能再引用其他实体

void TestRef()
{
 int a = 10;
 // int& ra; // 该条语句编译时会出错
 int& ra = a;
 int& rra = a;
 printf("%p %p %p\n", &a, &ra, &rra); 
}

对于常量的引用:

void TestConstRef()
{
 const int a = 10;
 //int& ra = a; // 该语句编译时会出错,a为常量
 const int& ra = a;
 // int& b = 10; // 该语句编译时会出错,b为常量
 const int& b = 10;
 double d = 12.34;
 //int& rd = d; // 该语句编译时会出错,类型不同
 const int& rd = d;
}

为什么不能直接引用一个常量呢?

这是因为引用的一个用处是,引用改变的时候实体也会跟着改变,因为它和引用实体占用同一个地址空间(就比如你让孙行者带上金箍,那孙悟空是不是也带上了金箍?)

int a = 10;
int& b = a;
cout << b << " " << a << endl;
b = 20;
cout << b << " " << a << endl;


可见a的值也被修改成了20。
但是常量之所以是常量,就是因为它不能被随意修改,如果用

const int a=10;
int& ra=a;

那么a就存在被修改的可能性,对于a来讲,权限就被放大了,但是这样的权限放大是很危险的,所以有一个原则:可以进行权限缩小或者平移,但是不能扩大。所以我们可以像下面这样:

//平移
int a=0;
int& ra=a;
//平移
const int b=10;
const int& rb=b;
//缩小
const int& ra1=a;

除了作为一个引用变量/常量,引用还有哪些使用场景呢?

  1. 作函数参数

之前在写C程序的交换函数时,因为形参是实参的一份拷贝,想要改变实参就要传地址,而现在有了引用就不用传地址了!

void Swap(int& left, int& right)//交换函数
{
   int temp = left;
   left = right;
   right = temp;
}

int a = 10;
int b = 20;
Swap(a,b);//因为形参为引用,所以这传的是a,b的引用,所以形参的改变就是实参的改变

但是注意,不要这样传参:Swap(&a,&b),因为这样传进去的是地址(兼容C语言),广泛地说,&跟在类型后面是引用,跟在变/常量的前面是取地址。

  1. 作返回值
    引用作为返回值时,可以在函数外面修改函数里面的内容,前提是引用的变量出了函数也不会销毁
static int n = 0;
int& Count1()
{
	n++;
	n++;
	return n;
}

int& Count2()
{
	int m=0;
	m++;
	return m;
}
int& tmp = Count1();
int& tem = Count2();
tmp = 20;
tem = 10;
cout << tmp << ' ' << n<<' '<<tem<<' '<<m;


这你Count2()函数所在的空间为栈空间,由函数栈帧的有关知识可知,进入函数时创建栈帧,m变量也在栈帧中,在出了函数之后其栈帧会被销毁,m的地址也会被释放,所以这是tmp的空间其实被释放过(我愿称之为野引用)而n由于存在静态区就不会被释放。
甚至给tem赋值也不会有变化:
在这里插入图片描述
所以如果实体在出函数后会被销毁的时候需要传值返回。

看到这,你有没有感觉这根指针很像?他们直接肯定有联系!
引用和指针的联系:
引用在语法概念上就是一个别名,和实体共用一份空间
但是引用在底层实现上是有空间的,因为引用是按照指针的方式来实现的,也就是说,指针的底层汇编和引用一样。


简直一模一样
但是它俩也有区别:

  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全,指针比引用更灵活
    总的来说,C++中更喜欢使用引用,特别在一些容器中比如栈、队列等,在类和对象中也十分常见。

3.6 空指针nullptr

在C++98中,NULL表示空指针,一般在我们初始化一个指针的时候会用NULL,但实际上,NULL是一个宏,在stddef.h中定义:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

可以看到,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,因此与程序的初衷相悖。
在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

但是在C++11中,nullptr作为一个关键字被引入,所以使用它时不需要包含头文件,而且它就是(void*)0,这里建议以后进行C++的编程时表示空指针都用nullptr

3.7 内联函数

什么叫内联函数?
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
在学习函数栈帧的时候我们了解到,在汇编代码中,函数的调用一定会用到call指令:

int Add(int left, int right)
{
	return left+right;
}


就是return 0 上面两行那个call

inline int Add(int left, int right)
{
	return left+right;
}


此时就没有call了,直接把Add函数内部的搬过来了
所以内联函数的本质就是,用函数体替换函数调用。
内联函数可以减少调用,提高运行效率,但因为可能把函数体多次展开,也可能使目标文件变大。
如果要是把一个很大的函数体多次展开,那岂不是十分冗杂?
编译器也想到了坐在电脑前的程序员可能是个笨蛋(比如我),所以它可以选择不展开。
inline对于编译器而言只是一个建议,若一个函数代码很长,则编译器不会将它变成内联

一般来说,函数代码在10行及以内时,这时编译器会将它优化为内联函数,有些编译器是在30行以内。
此外需要注意的是:
内联函数的定义和声明不能分开。因为inline被展开后,就没有函数地址了,链接时会找不到。

内联函数可以和宏替换有点类似,在某些宏替换的地方定义一个内联函数也可以

3.8 auto关键字

在C++的中后期,你可能会见到这样的代码:

__list_iterator<InputTterator>::iterator it = tmp.begin();

其中__list_iterator是模版
InputTterator:类型,用来实例化类
iterator:类的成员
it :变量
好长!太麻烦了!不想写这么多!
怎么办?
可以这样写:

__list_iterator<InputTterator>::iterator it = tmp.begin();
//化简后
auto it = tmp.begin();

为什么可以这样写?
这是因为,auto是一个特殊的类型,可以像int char那样使用,但是这个类型是由编译器自己推导出来的,比如:

int a = 10;
auto b = a;
auto c = 'a';

这里编译器推导出来 b是int,而c 是char。

auto还有一些其他的使用规则:
1.对指针和引用的区别

auto对指针来说“可有可无”

int x = 10;
auto a = &x;
auto* b = &x;

观察这段代码,a是int* 类型,此时auto也是int*,b也是int类型,此时auto就是int,所以如果对象是指针,使用auto时加/不加都可以

但对引用来说则必须加&

int x = 10;
auto& c = x;
auto d = x;

c的类型是int的引用,此时的auto是int;d的类型是int,此时的auto是int。所以如果要用auto创建一个引用变量,请加上&符号

2.一行多次定义
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错
因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量

auto a = 1, b = 2; //没问题
auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同

事实上,当第一个变量被推导成int后,第二个变量默认也是int.,但是int类型不能存放double类型的值,所以编译失败,因此也可以推断出:

auto a = 1, b = 2; //没问题
auto c = 3, d = 'a';  // 该行代码会编译失败,因为c和d的初始化表达式类型不同

是没有问题的

3.auto无法使用的地方
(1)函数参数

void TestAuto(auto a)
{
	//...
}

这个不用多说,因为编译器无法确定它的实际类型
(2)声明数组

int a[]={1,2,3};
auto b[]={2,3,4};

直觉上,似乎b数组里都是整型,这完全可以推导出来啊。但是事实上不能,原因
(其实auto只和下面讲的范围for在一起用的时候比较常见,其他时候不常见)

3.9 基于范围的for循环

看下面这个代码

int arr[] = {1,2,3,4,5,6};
for(auto e : arr)
{
	cout<<e <<" ";
}

咦?似乎跟我们平时用的for不太一样
是的,这就是C++11引入的范围for循环,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;
}

范围for循环的要求:

  1. for循环迭代的范围必须是确定的
    对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的
    方法,begin和end就是for循环迭代的范围。(后面会讲)
    以下代码就有问题,因为for的范围不确定:
void TestFor(int array[])
{
 for(auto& e : array)
 cout<< e <<endl;
}
  1. 迭代的对象要实现++和==的操作。(如果不能++和判相等还怎么迭代?)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116868.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

拓扑排序-广度优先遍历思路

本质&#xff1a; 【广度优先遍历 】【贪心算法】应用于【有向图】的专有名词 应用场景&#xff1a;任务调度&#xff0c;课程安排 作用&#xff1a; 得到一个不唯一的【拓扑序】检测【有向图】是否有环&#xff0c;使用数据【并查集】 使用&#xff1a;先找度为0的前驱节点…

Linux运维排查常见故障_在tmp目录下有大量包含picture_ 的临时文件,每天晚上2 30需要对一天前的文件进行

echo“”>>/etc/security/limits.conf echo“*softnproc65535″>>/etc/security/limits.conf echo“*hardnproc65535″>>/etc/security/limits.conf echo“*softnofile65535″>>/etc/security/limits.conf echo“*hardnofile65535″>>/etc/secur…

【自动驾驶】控制算法(八)横向控制Ⅲ | 代码与模型

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

以太网--TCP/IP协议(一)

概述 以太网是局域网的一种&#xff0c;其他的比如还有令牌环、FDDI。和局域网对应的就是广域网&#xff0c;如Internet&#xff0c;城域网等。 从网络层次看&#xff0c;局域网协议主要偏重于低层&#xff08;业内一般把物理层、数据链路层归为低层&#xff09;。以太网协议…

单片机毕业设计基于单片机的智能门禁系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍程序代码部分参考 设计清单具体实现截图参考文献设计获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP…

vue动态统计图的绘画

效果图&#xff1a; 实现&#xff1a; 一、导入依赖 import echarts from echarts 二、vue的代码实现 1.在main.js导入文件 // 引入 echarts 插件 import echarts from echarts // 配置成全局组件 Vue.prototype.$echarts echarts2.代码实现 <template><!--为echa…

韩国火烧车影响出现,浙江出现限制电车进入地下车库,车主难受了

韩国电动汽车起火&#xff0c;烧毁140辆汽车&#xff0c;还导致大楼损坏以及居民受伤的后果&#xff0c;如今在中国市场也产生了影响&#xff0c;《华商报》旗下的《大风新闻》报道指&#xff0c;浙江多地的饭店、大厦禁止电动汽车进入地下车库&#xff0c;这下子电动汽车车主又…

滑动窗口在算法中的应用

滑动窗口是一种经典的算法技巧&#xff0c;就像在处理一系列动态数据时&#xff0c;用一扇可以滑动的“窗口”来捕捉一段连续的子数组或子字符串。通过不断地移动窗口的起点或终点&#xff0c;我们能够以较低的时间复杂度来解决一系列问题。在这篇文章中&#xff0c;我们将通过…

图形视频处理软件Adobe After Effects(AE)2024WIN/MAC下载及系统要求

目录 一、Adobe AE软件简介 1.1 什么是Adobe AE软件 1.2 AE软件的发展历程 1.3 AE软件的应用领域 二、Adobe AE软件下载 2.1 下载 2.2 下载注意事项 三、Adobe AE软件系统要求 3.1 最低配置要求 3.2 推荐配置要求 3.3 显示器和分辨率 四、Adobe AE软件安装与使用 …

【MacOS】mac定位服务中删除已经卸载的软件

mac定位服务中删除已经卸载的软件 网上的帖子真不靠谱 直接右键 WeTypeSettings &#xff0c;查找位置&#xff0c;丢废纸篓即可&#xff01;会提示你卸载的&#xff01;

Pyramid: Real-Time LoRa Collision Decoding with Peak Tracking技术思考与解读

一点点个人的论文解读、技术理解&#xff0c;难免会有错误&#xff0c;欢迎大家一起交流和学习~~ &#x1f600;作者关于lora的系列文章从问题陈述到方法论的提出&#xff0c;再到实验评估&#xff0c;文章结构条理清晰&#xff0c;逻辑性强&#xff0c;并深入分析了LoRa信号处…

力扣刷题(5)

整数转罗马数字 整数转罗马数字-力扣 思路&#xff1a; 把各十百千位可能出现的情况都列出来&#xff0c;写成一个二维数组找出该数的各十百千位&#xff0c;与数组中的罗马元素对应 const char* ch[4][10]{{"", "I", "II", "III"…

webpack - 五大核心概念和基本配置(打包一个简单HTML页面)

// 五大核心概念 1. entry&#xff08;入口&#xff09; 指示Webpack从哪个文件开始打包2. output&#xff08;输出&#xff09; 指示Webpack打包完的文件输出到哪里去&#xff0c;如何命名等3. loader&#xff08;加载器&#xff09; webpack本身只能处理js&#xff0c;json等…

Bev pool 加速(2):自定义c++扩展

文章目录 1. c++扩展2. 案例2.1 案例12. 1.1 代码实现(1) c++ 文件(2) setup.py编写(3) python 代码编写2.1 案例1在bevfusion论文中,将bev_pooling定义为view transform中的效率瓶颈,bevfusion 主要就是对bev_pooling进行了加速,使得视图转换的速度提高了40倍,延迟从500ms…

charles配置安卓抓包(避坑版)

下载Charleshttps://www.charlesproxy.com/安装&#xff0c;疯狂点击下一步即可注册&#xff1a;打开Charles&#xff0c;选择“Help”菜单中的“Register Charles”&#xff0c;进网站生成密钥&#xff1a;https://www.zzzmode.com/mytools/charles/,将生成的密钥填入注册重启…

JavaScript练手小技巧:利用鼠标滚轮控制图片轮播

近日&#xff0c;在浏览网站的时候&#xff0c;发现了一个有意思的效果&#xff1a;一个图片轮播&#xff0c;通过上下滚动鼠标滚轮控制图片的上下切换。 于是就有了自己做一个的想法&#xff0c;顺带复习下鼠标滚轮事件。 鼠标滚轮事件&#xff0c;参考这篇文章&#xff1a;…

Vue 3 + Element Plus 封装单列控制编辑的可编辑表格组件

在Web应用开发中&#xff0c;经常需要提供表格数据的编辑功能。本文将介绍如何使用Vue 3结合Element Plus库来实现一个支持单列控制编辑功能的表格&#xff0c;并通过封装组件的形式提高代码的复用性。通过本教程&#xff0c;你将学会如何构建一个具备单列控制编辑功能的表格组…

Cloudways搭建WordPress外贸独立站完整教程(1)

验证邮件发送完成后&#xff0c;就等待Cloudways的回复邮件&#xff0c;一般24小时之内就会收到激活的邮件。 Cloudways账号升级 激活成功后还需要账户升级&#xff0c;Cloudways提供了为期3天的免费试用体验。如果在试用期结束之前未绑定信用卡以升级账户&#xff0c;试用期…

UE5学习笔记21-武器的射击功能

一、创建C类 创建武器子弹的类&#xff0c;创建生产武器子弹的类&#xff0c;创建弹壳的类&#xff0c;生产武器子弹的类的父类是武器的类 创建后如图&#xff0c;ProjectileMyWeapon类(产生子弹的类)继承自weapon类&#xff0c;Projectile(子弹的类)&#xff0c;Casing(弹壳声…

Claude 3.5:如何高效辅助编程——全面入门指南

在现代编程世界中&#xff0c;AI的角色越来越重要&#xff0c;尤其是在代码生成、调试、文档生成等领域中&#xff0c;AI工具的运用让开发者可以更高效地完成任务。Claude 3.5是一个这样的AI助手&#xff0c;凭借其强大的自然语言处理能力&#xff0c;在编程中提供了大量的支持…