当前位置: 首页 > news >正文

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

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); // 该语句编译出错,无法识别areturn 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. 迭代的对象要实现++和==的操作。(如果不能++和判相等还怎么迭代?)

相关文章:

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

1.C的发展史 C语言诞生后&#xff0c;很快普及使用&#xff0c;但是随着编程规模增大且越来越复杂&#xff0c;并且需要高度的抽象和建模时&#xff0c;C语言的诸多短板便表现了出来&#xff0c;为了解决软件危机&#xff0c;上世纪八十年代&#xff0c;计算机界提出了oop&…...

C 语言面试题大汇总之华为面试题

文章目录 1. 局部变量能否和全局变量重名?2. 如何引用一个已经定义过的全局变量?3. 全局变量可不可以定义在可被多个.C 文件包含的头文件中?为什么?4. 请写出下列代码的输出内容5. static 全局变量与普通的全局变量有什么区别?static 局部变量和普通局部变量有什么区别?s…...

Java:面向对象

继承 继承是一种面向对象编程&#xff08;OOP&#xff09;特性&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;继承另一个类&#xff08;称为父类或基类&#xff09;的属性&#xff08;如方法和字段&#xff09;。通过继承&#xff0c;子类可以复用父类的代…...

【区块链 + 基层治理】腾讯未来社区:区块链业主决策系统 | FISCO BCOS应用案例

腾讯未来社区是腾讯推出的智慧社区综合解决方案&#xff0c;致力于形成“互联网 社区”一站式解决方案&#xff0c;打造智慧社 区健康生态。为了解决物业管理领域的痛点&#xff0c;构建围绕居民、物业、政府和商业四个角色为核心的良好生态&#xff0c;以 信息平台及工具为纽…...

【Rust练习】13.数组

练习题来自&#xff1a;https://practice-zh.course.rs/compound-types/array.html 1 fn main() {// 使用合适的类型填空let arr: __ [1, 2, 3, 4, 5];// 修改以下代码&#xff0c;让它顺利运行assert!(arr.len() 4); }显然这个数组的长度是5. fn main() {// 使用合适的类…...

直流负载技术介绍

直流负载技术是一种用于控制和调节电力系统运行状态的重要技术。它主要通过对电力系统中的直流负载进行有效的管理和控制&#xff0c;以保证电力系统的稳定运行&#xff0c;提高电力系统的运行效率&#xff0c;降低电力系统的运行成本。 直流负载技术主要包括直流负载的检测、…...

FPGA低功耗设计

FPGA低功耗设计 文章目录 FPGA低功耗设计前言一、功耗类型1.1 动态功耗1.2 静态功耗1.3 浪涌功耗 二、系统级低功耗设计2.1 **多电压技术&#xff1a;**即工作频率、电压和功耗的关系2.2 系统时钟分配&#xff1a;2.3 软硬件划分2.4 p 或单元库选择 三、RTL级别低功耗设计3.1 并…...

Python Opencv: 基于颜色提取的印章分割

利用Python实现了一个图像处理功能&#xff0c;即批量提取图像中的印章区域&#xff1b;使用了颜色聚类的方法来提取颜色。 本代码也发布到了github&#xff0c;欢迎大家试用&#xff08;如果帮助&#xff0c;请star一下&#xff09;&#xff1a; GitHub - AICVHub/seal_seg_o…...

Codeforces Round 970 (Div. 3)(ABCDEF)

Codeforces Round 970 (Div. 3) A:Sakurakos Exams 签到 题意:给定1,2的数量,判断是否能用加减符号使得这些1,2计算出0 void solve() {cin>>n>>m;if(n%2)cout<<"NO\n";else{if(m%20||n)cout<<"YES\n";else cout<<"…...

springboot基于ssm+Jsp的人才招聘网站系统的设计与实现 jw2cs

目录 前言详细视频演示后端技术栈具体实现截图开发核心技术&#xff1a;开发工具核心代码部分展示系统设计操作可行性可行性论证试验方案源码获取 前言 &#x1f447;&#x1f3fb; 博主介绍&#xff1a;&#x1f447;&#x1f3fb; 全网粉丝50W,博客专家、CSDN特邀作者、CSDN…...

高质量共建“一带一路”!苏州金龙助力非洲交通驶向共同繁荣之旅

9月6日&#xff0c;中非合作论坛在北京落下帷幕。此次论坛&#xff0c;“高质量共建‘一带一路’”成为重要议题。截止至目前&#xff0c;苏州金龙海格客车已向阿尔及利亚、埃塞俄比亚、南非等所有参与共建“一带一路”的非洲国家累计出口客车14000台。从产品销售&#xff0c;到…...

嵌入式初学-C语言-数据结构--四

栈 1. 基本概念 栈是一种逻辑结构&#xff0c;是特殊的线性表。特殊在&#xff1a; 只能在固定的一端操作 只要满足上述条件&#xff0c;那么这种特殊的线性表就会呈现一种“后进先出”的逻辑&#xff0c;这种逻辑就被称为栈。栈 在生活中到处可见&#xff0c;比如堆叠的盘子…...

【HarmonyOS 4】应用性能优化

1. ArkTs 高性能编程 1.1 ArkTs 高性能编程规则 1.1.1 限制一些 TypeScript 的特性&#xff0c;比如需要不支持属性的动态变更、变量或参数需要明确的类型声明和返回值声明等。1.1.2 禁用 ts-ignore、ts-expect-error 等屏蔽编译校验的命令。1.1.3 开启 TypeScript 的严格模式…...

MySQL——表操作

目录 一、创建表 二、查看表 2.1 查看表中某成员的数据 2.2 查看整个表中的表成员 2.3 查看创建表时的句柄 三、修改表 alter 3.1 重命名 rename 3.2 新增一列 add 3.3 更改列属性 modify 3.4 更改列名称 change 3.5 删除某列 上一篇博客介绍了库的操作&#xff0c;…...

阅读笔记--Guiding Attention in End-to-End Driving Models(二)

端到端驾驶的注意力学习&#xff08;Attention Learning for End-to-End Driving&#xff09;关键内容学习 3.1 问题设置&#xff08;Problem Setup&#xff09; 模仿学习&#xff08;Imitation Learning, IL&#xff09;&#xff1a;介绍了模仿学习的概念&#xff0c;即通过…...

Linux: network: TCP: errno: EWOULDBLOCK

https://mzhan017.blog.csdn.net/article/details/108010013 这个errno的意思: 如果是send接口函数返回的错误,代表tcp socket的sending buffer满了,让应用程序等上一段时间重试send。 所以,这个产生的原因就不固定了: 可能是当前系统太忙,导致系统发包慢,buffer累积; 可…...

闲话“设计模式”

Q1、请详细介绍 软件架构设计模式&#xff08;智能化&#xff09;&#xff0c;应用程序设计模式&#xff08;自动化&#xff09;&#xff0c;编程语言设计模式&#xff08;人性化&#xff09;&#xff08;后面括号中 是我 希望 其 具有的特点&#xff09; 的概念&#xff0c;有…...

Sentence-BERT实现文本匹配【CoSENT损失】

引言 还是基于Sentence-BERT架构&#xff0c;或者说Bi-Encoder架构&#xff0c;但是本文使用的是苏神提出的CoSENT损失函数1。 点击来都是缘分&#xff0c;之前过时的方法可以不细看&#xff0c;别的文章可以不收藏&#xff0c;现在是最流行的方法&#xff0c;这篇文章建议收藏…...

业余考什么证书比较实用?

在业余时间里&#xff0c;获得一些有用的证书不仅能提升你的专业素养&#xff0c;还能增强你在职场上的竞争力。 特别是职业技能证书和行业认证证书&#xff0c;这两者受到了广大职场人士的高度关注。 一、业余时间考取的实用证书 行业认证证书主要针对特定行业或职业&#…...

16款facebook辅助工具,总有一款适合你!

Hey小伙伴们~&#x1f44b; 是不是想利用FB大展拳脚&#xff0c;却苦于不知道如何开始&#xff1f;别急&#xff0c;今天就给你们安利16个超实用的FB营销工具&#xff0c;涵盖了内容创建和发布的应用程序&#xff0c;以及数据追踪分析、商品销售等多个方面让你轻松get海外获客新…...

给网站发外链的好处,你了解多少?

在当今这个信息爆炸的互联网时代&#xff0c;网站优化和推广成为了每一个网站主不可忽视的重要环节。其中&#xff0c;给网站发外链&#xff0c;即在其他网站上设置指向自己网站的链接&#xff0c;是一种高效且被广泛采用的策略。那么&#xff0c;给网站发外链究竟能带来哪些好…...

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析&#xff1a; url中含有特殊字符 中文未编码 都有可能导致URL转换失败&#xff0c;所以需要对url编码处理 如下&#xff1a; guard let allowUrl webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时&a…...

excel分列

Excel中有这么几列&#xff0c;希望将每一列内容再分出3列&#xff1a; 可以通过以下步骤在 Excel 表格中将 B 到 F 列的内容拆分为每列的 3 列&#xff0c;分别为 pred_label、pred_score 和 pred_class&#xff1a; 确定数据结构&#xff1a;假设 B 列到 F 列中的内容都是按类…...

STM32 HAL DMA 中断碰到的问题

流程 串口收数据—>dma搬运到变量—>空闲中断----->接收完成 配置 dma中断全部去掉 串口中断开启 freertos中断全部去掉 时钟配置 代码 开启中断 // DMA 空闲检查 void receives_uaru_7(void) {RXU7 0;//清除中断标志HAL_UARTEx_ReceiveToIdle_DMA(&hua…...

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现&#xff0c;因为rasa本身是带有remindschedule模块的。不过经过一番折腾后&#xff0c;忽然发现&#xff0c;chatbot上实现的定时&#xff0c;语音助手不一定会有响应。因为&#xff0c;我目前语音助手的代码设置了长时间无应答会结束…...

AIoTedge边缘计算+边缘物联网平台

在数字化转型的浪潮中&#xff0c;AIoTedge边缘计算平台以其边云协同的架构和强大的分布式AIoT处理能力&#xff0c;正成为推动智能技术发展的关键力量。AIoTedge通过在数据源附近处理信息&#xff0c;实现低延迟、快速响应&#xff0c;增强了应用的实时性。同时&#xff0c;它…...

Java使用拷贝asset文件,解密,并用DexclassLoader加载执行

//asset中加密的apk文件重命名为index.html,拷贝到私有目录 //解密 //加载,执行apk中的方法 public static void handleByJava(Context context){File copyedFile new File(context.getFilesDir().getAbsolutePath() "/" "main.html");FileUtil.copyAss…...

【AcWing】861. 二分图的最大匹配(匈牙利算法)

匈牙利算法&#xff0c;他可以在比较快的时间复杂度之内告诉我们左边和右边成功匹配的最大数是多少 匹配指的是边的数量&#xff0c;成功的匹配指的是两个未被使用的点之间存在一条边(就不存在两条边共用了一个点的)。 匈牙利算法可以返回成功匹配的最大匹配数是多少。 #incl…...

经验笔记:JSP(JavaServer Pages)

JSP&#xff08;JavaServer Pages&#xff09;经验笔记 JSP&#xff08;JavaServer Pages&#xff09;是一种用于创建动态网页的技术&#xff0c;它允许在HTML页面中嵌入Java代码&#xff0c;从而实现动态内容的生成。JSP与Servlet一样&#xff0c;都是Java EE平台的一部分&am…...

【零基础必看的数据库教程】——SQL WHERE 子句

WHERE 子句用于提取那些满足指定条件的记录&#xff0c;过滤记录。 SQL WHERE 语法&#xff1a; SELECT column1, column2, ... FROM table_name WHERE condition; 参数说明&#xff1a; column1, column2, ...&#xff1a;要选择的字段名称&#xff0c;可以为多个字段。如…...