从零开始C++精讲:第一篇——C++入门

文章目录

  • 前言
  • 一、C++关键字
  • 二、命名空间
    • 2.1引子
    • 2.2命名空间定义
    • 2.3命名空间的使用
  • 三、C++输入和输出
    • 3.1输出
    • 3.2输入
  • 四、缺省参数
    • 4.1全缺省
    • 4.2半缺省
  • 五、函数重载
    • 5.1重载概念
  • 六、引用
    • 6.1定义
    • 6.2引用的使用示例
      • 6.2.1引用作参数
      • 6.2.1引用作返回值
    • 6.3传值、传引用效率比较
    • 6.4常引用
    • 6.5引用和指针的区别
  • 七、内联函数
  • 八、auto关键字
  • 九、基于范围的for循环
    • 9.1范围for的语法
    • 9.2 范围for的使用条件
  • 十、指针空值(nullptr)


前言

本篇C++入门主要是讲C++对C语言不足的一些弥补,把这部分学好,为我们以后C++进阶打好坚实的基础。

一、C++关键字

C++总计有63个关键字,其中C语言的就占了32个。

所谓关键字就是我们用来控制语法的关键标识符,比如类型、循环、判断等等
在这里插入图片描述

二、命名空间

2.1引子

我们先来看一个C里面的常见问题:
现在我想打印rand这个变量的值

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

在这里插入图片描述
可以看出,这里的代码是没有问题的,也正常打印了0这个值

但如果我们再加一个头文件#include<stdlib.h>

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

发现这里居然报错了
在这里插入图片描述
这里的报错原因是因为我们的stdlib这个头文件里面也有rand命名的函数,与我们这里命名的全局变量rand产生了冲突。

这就是C语言的一大缺陷,在我们的项目中,难免会有相同名字的变量、函数等,但是一旦命名相同,C语言就无法编译通过

所以,C++旨在改进这一缺陷,引入了“命名空间”这一概念
具体做法就是在指定的名称前加一个namespace,有点类似我们C语言学习的结构体

2.2命名空间定义

namespace test{//这里的命名空间叫test,你也可以取其他名字int rand = 0;
}//与结构体不一样的是,C++的命名空间不用加分号int main()
{printf("%d",rand);return 0;
}

在这里插入图片描述

那命名空间到底是什么东西?
我们大白话说就是,现在我家住在山上,我家里养了几头猪,然后山里也有一些野猪在这里插入图片描述
那我们为了区别到底是野猪还是家猪,那我们就装一个围栏,把我们的家猪给围起来。

这个围栏就是namespace

但是细心的同学会发现,这里还是有一个警告,这个警告其实就是告诉我们%d需要的是int型,但我们给的是指针类型,如果需要打印指针类型就是%p,

如果就是要打印rand的值,那我们就需要用到“围栏的钥匙”,也就是域作用限定符::

int main()
{printf("%p\n", rand);//%p打印的是地址printf("%d", test::rand);//如果要找到家猪rand,需要找到对应家猪的围栏test//这里的::就是域作用限定符,相当于围栏的钥匙return 0;
}

所以,有了命名空间,我们就可以解决命名冲突的问题,如果不同程序员定义了多个同名变量,那就设多个命名空间,通过域作用限定符来使用不同的同名变量

int main()
{printf("%d\n", test::rand);//如果要找到家猪,需要找到对应家猪的围栏//这里的::就是域作用限定符printf("%d\n", test1::rand);printf("%d\n", test2::rand);return 0;
}

在这里插入图片描述

2.3命名空间的使用

命名空间除了定义变量,也可以定义函数、结构体等

namespace test{//命名空间testint rand = 0;//可以定义变量int add(int x, int y)//可以定义函数{return x + y;}struct LNode {//可以定义结构体struct LNode* next;int val;};
}

命名空间就是建了一个围栏,你要找猪,默认不会去围栏里去找。如果想要找围栏里的猪,请给出“围栏名称和围栏钥匙”(也就是命名空间的名字和作用域限定符::)

比如这里我想调用add函数,但是没有给命名空间和作用域限定符,编译器就会报错
在这里插入图片描述

我们把命名空间加上,代码就可以正确运行了

namespace test{//命名空间testint rand = 0;//可以定义变量int add(int x, int y)//可以定义函数{return x + y;}struct LNode {//可以定义结构体struct LNode* next;int val;};
}int main()
{int x=test::add(1, 2);printf("%d", x);return 0;
}

在这里插入图片描述

命名空间的结构体也是类似的,比如
struct test::LNode node;
相当于是struct LNode node,
只不过这里中间的LNode是定义在test里,所以我们把LNode换成test::LNode

namespace test{//命名空间testint rand = 0;//可以定义变量int add(int x, int y)//可以定义函数{return x + y;}struct LNode {//可以定义结构体struct LNode* next;int val;};
}int main()
{struct  test::LNode node;//相当于是struct LNode node,//只不过这里中间的LNode是定义在test里,所以我们把LNode换成test::LNodereturn 0;
}

可能会有老铁问:“那如果我命名空间里的变量同名产生冲突怎么办?”

答:那你就在命名空间里再定义一个命名空间呗,每有一层命名空间,就多用一个作用域限定符::

namespace test{//命名空间testint rand = 0;//可以定义变量namespace test1 {int rand = 1;}
}int main()
{int x = test::rand;int y = test::test1::rand;printf("x=%d\n", x);printf("y=%d\n", y);return 0;
}

在这里插入图片描述

但是这样做的话还有一个问题,你每次想要用自己定义的,在命名空间那个变量/函数/结构体,都要加上命名空间和作用域限定符::,这你不觉得烦吗?

所以我们这里可以用

using namespace 命名空间名称;

这样就默认了到某个命名空间里去用它的变量/函数/结构体

使用了这种,你可以直接访问命名空间的,也可以用::来访问

#include<stdio.h>namespace test{//命名空间testint rand = 0;//可以定义变量
}using namespace test;
int main()
{int x = test::rand;int y = rand;printf("x=%d\n", x);printf("y=%d\n", y);return 0;
}

在这里插入图片描述

这就有了我们经常用的

using namespace std;

std是我们c++官方定义的命名空间

ps:在工程项目中不要轻易展开std库,因为很容易你自己写的和库中的冲突。日常自己练习一般无所谓。

三、C++输入和输出

3.1输出

到这里,大家可能会发现,我们前面好像打印输出函数还是我们c语言的那一套,那我们c++有没有自己的呢?有!

#include<iostream>
using namespace std;
int main()
{cout << "hello world" ;//“<<”是流插入运算符,cout是std库里的一个//c是console控制台的意思,out是出来的意思//这里涉及到c++的对象知识,我们先学怎么用,具体的原理后面学习面向对象我会详细介绍//你可以理解为"hello world"流向了cout控制台//cout很牛逼在于,它可以自动识别类型int a = 1;float b = 3.14;char c = 'x';cout << a<<"\n";//如果要换行,就在后面跟一个<<"\n",表示\n也流向了cout控制台cout << b<<endl;//换行法二:在后面加一个<<endl表示换行,法二是最常见的,法一有点麻烦了//endl表示end 这个line,就是结束这一行的意思cout << c<<endl;//你也可以一次性全部流入,比如cout << a << b << c;return 0;
}

在这里插入图片描述
ps:我们直接using namespace std其实是风险很大的,因为你一个人直接展开库里面的全部,就会导致别人很可能定义的变量和命名空间里的产生冲突,所以我们这里建议只展开std里面的cout和endl

using  std::cout;
using  std::endl;

另外还有一点,如果你想控制打印出来的精度,比如我想打印一个浮点数,只到小数点后1位,c++是不太方便的,这里你还是用c语言的printf来控制(c语言能用的c++也可以用)

int main()
{float x = 3.1415926;printf("%.2f", x);
}

在这里插入图片描述

3.2输入

这里的输入也就对应了C语言的scanf
我们c++里面就是cin
c是console控制台的意思,in是进去的意思

需要注意的是,因为我们这里是输入嘛,所以用cin>>,这里的符号和输出也是反过来的

using std::cout;
using std::endl;
using std::cin;
int main()
{int a = 0;cout <<"请输入a值:";cin >> a;cout << "a值为"<<a;//cout叫流插入//cin叫流提取,它同样可以自动识别类型
}

在这里插入图片描述

四、缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参

4.1全缺省

所谓全缺省,就是你定义的函数,这个函数的参数你都预先赋了值

using namespace std;
void func(int a = 1,int b=2, int c=3) {cout << a << endl;cout << b << endl;cout << c << endl;
}
int main() {func();//缺省a和b和c,默认a=1,b=2,c=3func(10);//缺省b和c,默认b=2,c=3func(10,20);//缺省c,默认c=3func(10,20,30);
}

在这里插入图片描述

4.2半缺省

这种缺省方式就是有一部分参数预先赋了值

注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数声明和定义中同时出现
  3. 缺省值必须是常量或者全局变量
  4. C语言不支持(编译器不支持)

在这里插入图片描述
如果采用了半缺省,你就不能一个参不传了。
因为半缺省肯定是有参数没有提前定义的,如果你一个参数不传,那那个没有提前定义的参数就没有值给它了,就会报错。

using namespace std;
void func(int a ,int b=2, int c=3) {cout << a << endl;cout << b << endl;cout << c << endl;
}
int main() {func(10);//10传参给a;缺省b和c,默认b=2,c=3func(10,20);//10和20传参给a和b;缺省c,默认c=3func(10,20,30);//10,20,30传参给a,b,c
}

在这里插入图片描述

五、函数重载

函数重载有点像我们中文的“一词多义”

5.1重载概念

说白了就是函数名相同,参数类型不一样、个数不一样、参数顺序不一样返回值可以不一样,可以一样

ps:C语言是不支持同名函数的,但是我们c++可以支持,只是有一些限制

using namespace std;
int add(int a, int b) {cout << "int add(int a,int b)" << endl;return a + b;
}double add(double a, double b) {cout << "double add(double a, double b)" << endl;return a + b;;
}int main()
{int x=add(1, 2);cout << x << endl;double y=add(1.1, 2.2);cout << y << endl;return 0;
}

在这里插入图片描述

小细节:如果你有两个不同的重载,这时你传的参数和这两个重载都不一样,比如
在这里插入图片描述
这里就会有歧义了,因为有两个重载,你到底是把int类型的1转成double还是把double类型的2.2转成int类型呢?所以这里会报错

但是如果你只有一个函数,那你这样传就没有毛病了,编译器会自动给你进行类型转换
(因为现在只有一个函数了,只要把传参过去类型不对的转换一下就行,没有重载函数的歧义)
在这里插入图片描述

六、引用

6.1定义

这是对C语言改进最大的地方,C++里面就没有指针的概念了(当年C++的祖师爷也觉得指针过于复杂)

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。

using namespace std;
int main()
{int a = 1;int& b = a;int& c = b;cout << a << endl;cout << b << endl;cout << c << endl;cout << &a << endl;cout << &b << endl;cout << &c << endl;return 0;
}

可以看到abc共享一个空间,值也是一样的
在这里插入图片描述

比如现在有a、b、c三个变量
在这里插入图片描述
b是把a的值赋过去,b和a不是一个变量,它们独占不同空间

但是我们这里int& c=a,这里就不一样了,c其实就是a的别名,
c和a其实是一个东西,它们占用同一份空间

如果你这里b++,那么a不会受到任何影响
在这里插入图片描述
但如果你对c–,不好意思,a就是c,c就是a,即a–
在这里插入图片描述
ps:引用必须初始化,如果你int& b,不给b初始化,谁知道这个b是哪个变量的别名,所以c++的引用必须初始化。

c++的别名一旦确定,中途是不能改变对象的(比如a起了别名b,那b就一直是a的别名)

int main()
{int a = 0;int& b = a;int c = 1;b = c;//这里是把c值赋给b,还是让b称为c的别名?cout << b << endl;//打印1cout << &a << endl;//a和b同地址,a和c不同地址cout << &b << endl;cout << &c << endl;//说明b=c是赋值,是把c的值赋给b(b是a的别名,也就是把c的值赋给a)//但是b还是a的引用
}

6.2引用的使用示例

6.2.1引用作参数

我们c语言经常写一个交换函数swap

int swap(int* x,int* y){int tmp=*x;*x=*y;*y=tmp;
}

那我们c++就不需要这么麻烦了,我们之前说过,引用就是取别名,引用就是它自己,它自己就是引用。

所以我们c++这里swap函数就很容易写了

void swap(int& x, int& y) {int tmp = x;x = y;y = tmp;
}
int main()
{int x = 1;int y = 2;swap(x, y);cout<<"x=" << x << endl;cout<<"y=" << y << endl;return 0;
}

在这里插入图片描述

6.2.1引用作返回值

如果不是引用作为返回值,比如下面代码
我们很容易知道x的是1

int count()
{int n = 0;n++;return n;
}
int main()
{int x = count();cout << x << endl;return 0;
}

这段代码,我们先是在main函数里创立了ret变量,然后调用Count函数
在这里插入图片描述
创立Count函数栈帧,Count里又会创建变量n
在这里插入图片描述
这里Count函数调用完,栈帧就销毁了,所以我们返回的其实是n的复制,返回值为1。
(因为你栈帧已经销毁了,n也肯定销毁了,你再返回n的位置不就是野指针吗?)
在这里插入图片描述
但如果我们用的是引用返回
我们知道引用是我们某个变量的别名,引用就是那个变量
那你返回引用就是返回那个变量

在这里插入图片描述

可以看到,如果我们用引用做返回值,这里是报了一个警告的
因为你count函数结束,其实栈帧已经销毁了,
如果你还要访问原先函数里的变量,其实是非法访问。

至于这里返回值和前面的一样都是1,只是因为vs这个编译器是1,如果换别的编译器结果就不一定
如果用引用做返回值,返回值是不确定的
因为你用引用做返回值,返回值到底是多少是取决于函数栈帧销毁后,到底有没有把原先的空间清理掉,如果清理掉,那就不一定是原来的值了。我们这里vs编译器函数栈帧销毁后是没有清理原先空间,至于其他编译器就未可知了。

比如下面这个示例,大家就会对我上面说的话有更深的理解

int& count()
{int n = 0;n++;return n;
}
int main()
{int& x = count();cout << x << endl;cout << x << endl;count();cout << x << endl;return 0;
}

在这里插入图片描述
我们这里用引用x来接收,count函数返回的引用,这就意味着x和n是一个位置的
第一次我们打印了1,那是因为count函数栈帧里的东西还没销毁
第二次打印了一个1462811616,这个数就是count栈帧里东西销毁了,这个数可能是其他程序当时正在用那个位置生成的数。

第三次,由于我们又调用了count,导致n那个位置又生成了1,所以我们第三次打印出来是1

再举一个例子

int& add(int a,int b)
{int c = a + b;return c;
}
int main()
{int& x = add(1,2);cout << x << endl;add(3,4);cout << x << endl;return 0;
}

在这里插入图片描述
这里我们用x来接收add函数的引用
第一次我们x=add(1,2),并且由于编译器原因,函数结束栈帧销毁,原先变量位置并没有清除,所以打印了3

但第二次我们没有对x做任何改变,只是中途调用了add(3,4)
为什么打印x是7?
因为x接收的是add函数返回的c的引用,x和c其实是一个位置的同一变量
x其实就是c,所以第二次c改变了,x也是跟着改变,x=7

上面的3和7都是建立在vs这个编译器在函数栈帧销毁后没有清除原先变量空间的前提下,如果是不同编译器,打印情况应该如下:
在这里插入图片描述

这里为什么第二次调用add,x和c还是同一个位置的同一变量?
因为你再一次创建函数栈帧,函数栈帧还是那个函数栈帧,函数里的变量位置也还是不变的,
如下代码所示:

void func()
{int c = 0;cout << &c << endl;
}
int main()
{func();func();return 0;
}

在这里插入图片描述
再给大家看一个神奇的东西:

void func1()
{int c = 0;cout << &c << endl;
}void func2()
{int a = 0;cout << &a << endl;
}
int main()
{func1();func2();return 0;
}

在这里插入图片描述
并不是说同一个函数才能用同一个空间,不同函数也是用的一个同一块空间
只不过说不同函数它可能里面变量个数不同,它占用同一块空间的大小不同。

可以发现,我们虽然上面这样写,但是其实这个代码是有逻辑上的危险的
那什么时候可以用引用返回是安全的呢?你要么用静态的变量,或者栈上的,你malloc的。简而言之就是出了作用域,那个变量不会销毁,那你用引用就是安全的。

对于静态变量,我们来看一个例子:

int& add(int a, int b) {static int c = a + b;return c;
}
int main()
{int& x=add(1,2);cout << x << endl;add(3, 4);cout << x << endl;return 0;
}

在这里插入图片描述
这里x是c的别名,c又是一个局部静态变量局部静态变量只会被初始化一次
第一次调用add(1,2),我们初始化了局部静态变量c为3

第二次调用add(3,4),由于局部静态变量c已经被初始化过了,
所以第二次是不执行static int c = a + b;的

所以我们的x也就是c的别名,还是3

再来看另外一种情况:

int& add(int a, int b) {static int c = 0;c = a + b;return c;
}
int main()
{int& x=add(1,2);cout << x << endl;add(3, 4);cout << x << endl;return 0;
}

在这里插入图片描述
如果这样写,我们局部静态变量只会被初始化一次,但是c=a+b;是每次都要调用的
所以我们第一次打印了3,第二次打印了7。
看似两个情况是相同的代码,其实内在是完全不同的。

6.3传值、传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
在这里插入图片描述
为什么能提高效率,大白话就是,如果你传值返回,如果那个值是个特别大的结构体,那返回消耗的代价肯定也很大。但如果你传引用返回,其实就和返回一个指针差不多,占用空间会小很多的。

6.4常引用

在这里插入图片描述
如果一个变量已经被const修饰了,也就说它已经不能被修改了
如果这时你还对常变量取别名是有问题的。
因为我常变量a本身已经不能修改了,但是你给我取个别名b还能修改,这就产生矛盾了。

所以如果想给常变量取别名,别名也必须是const修饰的,如下图,这样就不会报错了
在这里插入图片描述

另外,虽然说const修饰的变量必须用const修饰的别名

但是,没有const修饰的变量也可以用const修饰的别名
(有点类似于权限不能放大,但是权限可以缩小)
在这里插入图片描述
还有一个需要提醒的点,这个大家应该不说也清楚。
就是不同类型的引用也是不可以互相用的
在这里插入图片描述
比如这里j是doible类型的,你可以把i强转,赋值给j
但是rj是double类型的引用,它只能是double类型变量的别名,你int类型的i就不可以用rj这个别名

进一步的,如果我们在double&前加const,这里又不报错了。
在这里插入图片描述
在这里插入图片描述
这是因为我们在强转的时候,会默认生成一个临时变量,通过把int类型的先变成double类型的临时变量,再把临时变量赋给double类型的变量。

而生成的临时变量又具有常变量的性质,所以我们const double& rj=i是可以的。相当于先产生一个const double类型的临时变量,再把这个临时变量赋给rj。

6.5引用和指针的区别

语法上,我们c++的引用就是原来的变量,它们共享一块空间
但是底层实现其实引用和指针是一样,其实是开辟了额外空间的

int main()
{int a = 10;int& b = a;//语法上b没有开额外空间,b就是a,a就是b//但是底层实现其实开了空间return 0;
}

下面是反汇编的部分截图
在这里插入图片描述

所以,引用底层实现就是指针,但是我们语法上默认c++的引用就是和原先变量共享一块空间。

举个例子

int main()
{char ch = 'x';char& c = ch;cout << sizeof(c) << endl;return 0;
}

按道理这里c其实底层是个指针,但是我们语法上还是默认引用就是原先变量
所以这里sizeof(c)其实就是sizeof(ch),也就是1
在这里插入图片描述
引用和指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

  2. 引用在定义时必须初始化,指针没有要求

  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
    一个同类型实体

  4. 没有NULL引用,但有NULL指针

  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
    位平台下占4个字节)

  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

  7. 有多级指针,但是没有多级引用

  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

  9. 引用比指针使用起来相对更安全
    ps:引用不是百分百安全的,比如前面讲的,你对出了某个作用域就销毁的变量进行引用,就会产生问题。

七、内联函数

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率

你就简单记住,写函数的时候,在返回值类型前加一个inline,就可以提升效率。其他的和正常写函数没啥区别

inline int add(int x, int y) {int z = x + y;return z;
}
int main()
{int x = add(1, 2);cout << x << endl;return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ps:内联更适合小函数,如果是那种代码比较多的大函数或者递归的函数,用内联就不合适。就算你大函数加内联,编译器也会默认大函数没有加内联。

八、auto关键字

auto可以自动推倒类型

int main()
{int a = 0;int b = a;auto c = a;//auto可以自动推倒类型,这里把int型的a赋给c,那么就默认c是int型auto d = &a;//指针可以显示的写,也可以不显示的写auto* e = &a;auto& f = a;//引用必须显示的写f++;cout << typeid(c).name() << endl;//typeid(变量名).name()可以用于打印一个变量的类型cout << typeid(d).name() << endl;//d是int型a的指针,所以打印int *cout << typeid(e).name() << endl;//指针可以显示的写,也可以不显示的写,所以这里auto d=&a和auto*e=&a,d和e的类型是一样的cout << typeid(f).name() << endl;//f是a的引用,f和a的类型一样都是intreturn 0;
}

在这里插入图片描述
但是上面的代码,说实话没有体现出auto真正的意义,它真正的作业需要到vector顺序表那块知识才能体现出来。比如下面这段代码:定义对象时,类型较长,用auto比较方便,但是auto不能作参数,也不能做返回值(有些比较新版本支持做返回值,但是强烈不建议做返回值,你返回一个值,都不知道是什么类型,我怎么知道用什么接收?)
auto可以让一个很长的定义变得很短哈哈哈哈,这段代码如果暂时看不懂没关系,后面会详细讲
在这里插入图片描述

另外,auto是不能用来声明数组的,别问为什么,当时写c++这么语言的创始人是这么规定的。

九、基于范围的for循环

9.1范围for的语法

在C++98中如果要遍历一个数组,可以按照以下方式进行:

int main()
{int arr[] = { 1,2,3,4,5 };//对数组的三种遍历方法//法一for (int i = 0;i < sizeof(arr) / sizeof(arr[0]);i++) {arr[i] *= 2;}//法二for (int* p = arr;p < arr + sizeof(arr) / sizeof(arr[0]);p++) {cout << *p<<" ";}cout << endl;//法三for (auto e : arr) {//会依次取数组arr中的数据赋给e对象,自动判断结束,自动++循环往后走cout << e <<" ";}
}

在这里插入图片描述
需要注意的是,法三这个是对赋值后的e进行打印,并没有对原先的数进行更改,比如:

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

在这里插入图片描述
当然了,如果你是用引用来接收数组数据,那么数组中的元素是会跟者变化的

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

在这里插入图片描述

9.2 范围for的使用条件

  1. for循环迭代的范围必须是确定的
    对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供
    begin和end的方法,begin和end就是for循环迭代的范围。
    注意:以下代码就有问题,因为for的范围不确定
void TestFor(int array[])
{for(auto& e : array)cout<< e <<endl;
}
  1. 迭代的对象要实现++和==的操作。(关于迭代器这个问题,以后会讲,现在提一下,没办法
    讲清楚,现在大家了解一下就可以了)

十、指针空值(nullptr)


void func(int) {cout << "f(int)" << endl;
}void func(int*) {cout << "f(int*)" << endl;
}
int main()
{int* ptr = NULL;func(0);//调用int那个函数func(NULL);//这里依旧是调用了int那个函数//NULL实际是个宏,在传统c语言头文件stddef.h中,NULL本质就是0//这个是当时写这个库的时候,那个创作者定义的func(ptr);//调用int*那个函数func(nullptr);//调用int*那个函数//我们一般初始化空指针也是用nullptrreturn 0;
}

在这里插入图片描述


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

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

相关文章

超维空间M1无人机使用说明书——01、ROS机载电脑使用说明——远程连接

引言&#xff1a;远程连接通常采用两种方式&#xff0c;一种是通过可视化软件&#xff0c;如VNC、Nomachine等&#xff0c;另外一种是使用SSH。各有优缺点&#xff0c;两种远程登录方式的优缺点做一个简单的对比&#xff1a; 1、SSH优缺点 优点:1、消耗网络资源 2、运行稳定 …

前端面试题集合六(高频)

1、vue实现双向数据绑定原理是什么&#xff1f; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>…

java SSM问卷调查系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM问卷调查管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代 码和数据库&#xff0c;系统主要采…

每天刷两道题——第十一天

1.1滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], k 3 输出&…

面试宝典之微服务框架面试题

S1、集群与分布式有啥区别&#xff1f; &#xff08;1&#xff09;相同点&#xff1a; 分布式和集群都是需要有很多节点服务器通过网络协同工作完成整体的任务目标。 &#xff08;2&#xff09;不同点&#xff1a; 分布式是指将业务系统进行拆分&#xff0c;即分布式的每一个…

Pycharm中如何配置python环境(conda)

首先在pycharm中点击 "File" > "Settings" 再次点击如下操作&#xff1a; 点击Python Interpreter的最右侧按钮&#xff0c;点击Show All... 找到python文件 最后点击OK

若依项目的table列表中对每一个字段增加排序按钮(单体版和前后端分离版)

一、目标&#xff1a;每一个字段都添加上下箭头用来排序 只需要更改前端代码&#xff0c;不需要更改后端代码&#xff0c;后面会讲解原理 二、单体版实现方式&#xff1a; 1.在options中添加sortable:true 2.在需要排序的字段中添加sortable:true 三、前后端分离版 1.el-tab…

Open CASCADE学习|非线性方程组

非线性方程组是一组包含非线性数学表达式的方程&#xff0c;即方程中含有未知数的非线性项。解这类方程组通常比解线性方程组更为复杂和困难。 非线性方程组在很多领域都有应用&#xff0c;例如物理学、工程学、经济学等。解决非线性方程组的方法有很多种&#xff0c;包括数值…

面试题-DAG 有向无环图

有向无环图用于解决前后依赖问题&#xff0c;在Apollo中用于各个组件的依赖管理。 在算法面试中&#xff0c;有很多相关题目 比如排课问题&#xff0c;有先修课比如启动问题&#xff0c;需要先启动1&#xff0c;才能启动2 概念 顶点&#xff1a; 图中的一个点&#xff0c;比…

09、Kafka ------ 通过修改保存时间来删除消息(retention.ms 配置)

目录 通过修改保存时间来删除消息★ 删除指定主题的消息演示1、修改kafka检查过期消息的时间间隔2、修改主题下消息的过期时间3、查看修改是否生效4、先查看下主题下有没有消息5、添加几条消息看效果6、查看消息是否被删除 ★ 恢复主题的retention.ms配置1、先查看没修改前的te…

【开源】基于JAVA语言的教学过程管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2.3.1 教师功能如下2.3.2 学生功能如下 三、系统展示 四、核心代码4.1 查询签到4.2 签到4.3 查询任务4.4 查询课程4.5 生成课程成绩 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVu…

云平台架构知识点总结

云平台架构 内部特供 版权所有 翻版必究 项目1 云计算的认知与体验 1.1 云计算定义 ​ 维基百科定义:云计算将IT相关的能力以服务的方式提供给用户&#xff0c;允许用户在不了解提供服务的技术﹑没有相关知识以及设备操作能力的情况下&#xff0c;通过Internet 获取需要的…

浅谈有源滤波在某棉纺企业低压配电室节能应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 0引言 在电力系统中&#xff0c;谐波的根本原因是非线性负载。由于电子设备、变频器、整流器等。在棉纺企业的大部分设备中被广泛使用&#xff0c;因此会产生大量的谐波电流。我公司的环锭纱线和筒车间的配电设备受到谐波的影…

c语言:输入成绩,统计不及格人数|练习题

一、题目 输入学生成绩&#xff0c;统计不及格的学生人数 二、代码截图【带注释】 三、源代码【带注释】 #include <stdio.h> //题目&#xff1a;输入成绩&#xff0c;统计不及格人数 //思考分析 //1、由于学生人数是未知数&#xff0c;所以可以在输入时&#xff0c;以0…

TextDiffuser-2:超越DALLE-3的文本图像融合技术

概述 近年来&#xff0c;扩散模型在图像生成领域取得了显著进展&#xff0c;但在文本图像融合方面依然存在挑战。TextDiffuser-2的出现&#xff0c;标志着在这一领域的一个重要突破&#xff0c;它成功地结合了大型语言模型的能力&#xff0c;以实现更高效、多样化且美观的文本…

前端开发Docker了解

1&#xff0c;docker简介 docker主要解决了最初软件开发环境配置的困难&#xff0c;完善了虚拟机部署的资源占用多&#xff0c;启动慢等缺点&#xff0c;保证了一致的运行环境&#xff0c;可以更轻松的维护和扩展。docker在linux容器的基础上进行了进一步的封装&#xff0c;提…

Java--业务场景:获取请求的ip属地信息

文章目录 前言步骤在pom文件中引入下列依赖IpUtil工具类在Controller层编写接口&#xff0c;获取请求的IP属地测试接口 IpInfo类中的方法 前言 很多时候&#xff0c;项目里需要展示用户的IP属地信息&#xff0c;所以这篇文章就记录一下如何在Java Spring boot项目里获取请求的…

springboot集成jsp

首先pom中引入依赖包 <!--引入servlet--> <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId> </dependency> <!--引入jstl标签库--> <dependency><groupId>javax.servle…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷③

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷3 目录 需要竞赛软件包环境以及备赛资源可私信博主&#xff01;&#xff01;&#xff01; 2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷3 模块一 …

springboot2.7集成sharding-jdbc4.1.1实现业务分表

1、引入maven <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><version>4.1.1</version></dependency> 2、基本代码示例 基本逻辑&#xff1a;利用数…