C++入门(下)--《Hello C++ World!》(2)(C/C++)

前言

这期会把上一期C++入门没有讲完的引用,内联函数跟nullptr给讲完,下期将会讲解类和对象
关于 C++的文章通通收录在了这个专栏里面:
在这里插入图片描述

引用

概念:引用是给已存在变量取了一个别名,它和它引用的变量共用同一块内存空间。

规则:

1.引用在定义时必须初始化

2.一个变量可以有多个引用

3.引用一旦引用一个实体,再不能引用其他实体

4.引用过程中,权限不可以扩大,只能平移和缩小(权限指的是eg:以后可以既读又改,现在只能读了)

使用场景:

1.作为函数的形参

1.做输出型参数–想里面改变,对应外面也改变的那种

2.减少拷贝,提高效率–对大对象(参数很多的那种,比如:一万个形参都用引用)或者深拷贝对象有用

2.作为函数的返回值

1.减少拷贝,提高效率–对大对象(参数很多的那种,比如:一万个形参都用引用)或者深拷贝对象有用(传值返回用拷贝,传引用返回不要拷贝)

2.修改返回值/获取返回值

 使用举例:
int a = 10;
int& ra = a;

int* x = &a;
int*& p = x;

引用一旦引用一个实体,再不能引用其他实体:举例:
int a = 10;
int& ra = a;
int b = 11;
ra = b;//这句话是改变ra的值,不是让ra成为b的引用

一个变量可以有多个引用,举例:
int a =0;
int& b = a;
int& c= b;


引用过程中,权限不可以扩大,只能平移和缩小,举例:(这个点属于常引用的)
  const int a = 0;
  int& b = a;//这个是不行的,扩大了

int x = 0;
int& y = x;
const int& z = x;//这里是权限缩小
x++;//这里会间接导致z++,但是z不能直接自己++

const int& m = 10;这样也是允许的
但是int& m =10;就不行
引用作函数返回值:
先举个例:
非引用:
int Count()
{
  int n = 0;
 n++;
return n;//这个的话是靠创建临时变量(变量占字节小的时候才是用寄存器)去过渡
}


引用:
int& Count()
{
  int n = 0;
 n++;
return n;
}
int main()
{
int ret = Count();
}

这样的话,分两种情况:
1.如果Count函数结束,栈帧销毁,没有清理栈帧的话,那么ret的结果侥幸是正确的

2.如果Count函数结束,栈帧销毁,清理栈帧,那么ret的结果是随机值
(特别是Count用了之后还进行了其他操作去用栈帧)

总结:引用做返回值时,出了函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回
eg:
int& Count()
{
 static int n = 0;
 n++;
return n;//这里返回的相当于是n的别名
}
int& SLAt(SeqList*ps,int pos)
{
return ps->a[pos];
}
易错点:
int& Count()
{
	int n = 0;
	n++;
	// ...
	return n;
}

int main()
{
	int ret = Count();//这里的ret是n的拷贝,不是引用,要int& ret = Count();才是
	return 0;
}

临时变量的一个小知识点

编译器为了执行代码搞的大部分临时变量都具有常性

举例: double dd = 12.21;
     int& i = dd;//这时就会报错
  
    但是
double dd = 12.21;
int i1 = dd;
int& i = i1;//这就不会报错



int func()
{
 static int x = 0;
 return x;
}
main函数里面int& ret = func();//这样就不行
         要const int& ret = func();//这样才行

常见情况:

1.发生类型转换时会产生临时变量来辅助,这时的就具有常性

2.传值引用返回值返回时也会产生临时变量,也有常性

3.隐式转换

(临时变量没有常性的情况以后才会提及)

引用和指针的区别

语法层面:

引用是不开空间,是对a取别名 指针是开空间,存储a的地址

从底层汇编指令实现的角度来看:引用是类似指针的方式实现的–也就是引用也会开空间

平时理解:引用的语法理解当不开空间,要是谈到底层的话,那就是会开空间

引用和指针的不同点:
1.引用概念上定义一个变量的别名,指针存储一个变量地址。
2.引用在定义时必须初始化,指针没有要求
3.引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
4.没有NULL引用,但有NULL指针
5.在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
6.引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7.有多级指针,但是没有多级引用
8.访问实体方式不同,指针需要显式解引用,引用编译器自己处理(底层)
9.引用比指针使用起来相对更安全

内联函数

内联函数

概念:以inline修饰的函数叫做内联函数

作用:在编译器编译时,会在调用内联函数的地方把内联函数在那展开(不会像自定义函数一样是通过call搞得),这样可以避免函数调用建立栈帧的开销

适用范围:适用于短小并且频繁调用的函数。

原因:如果不频繁的话,用的效果不明显;函数比较长的话,在编译过程中的指令会非常长,导致可执行程序的占用内存会很大(现在编译器可以主动避免这个了)

inline对于编译器只是一个建议,最终是否成为inline,是由编译器自己决定的

编译器会ban掉的一些函数(不让他成为内联,让他就是普通的函数):

1.比较长的函数

2.大多数递归函数

注意:深度深的递归跟频繁调用的函数是有差别的,不能等同:深度深的递归eg:1000*999一直乘到1这样一下算下来的话会导致文件变得很大,频繁调用函数则不会

默认debug模式下,inline在编译过程中不会起作用,否则就不方便调试了

在release版本下才会起作用,但是release版本下又看不了编译,这个需要自己去设置(不同软件的设置方法不一样)

注意:inline不建议声明和定义分离(要在同一文件下),分离会导致链接错误–原因:inline被展开之后,定义就没有函数地址了,链接就找不到那个函数的地址,就会报错

内联函数展示:
inline f(int a)
{
  return a*10;
}//也就在前面加个inline就行了

遗忘的补充:宏函数

优点:不需要建立栈帧,提高调用效率

缺点:复杂,容易出错;可读性差;不能调试(因为预编译阶段进行了替换)

C++中替代宏函数的方法:内联函数

举例:
正确写法:#define f(x,y) ((x)+(y))
易错点:1.后面加了;号和return
      2.f(x,y)写成了f(int x, int y)  
      3.x和y没有加()以及大整体没有加括号--出错原因:万一是f(a|b,a&b)
      隐藏知识点:+比&和|的优先级高

指针空值—nullptr

在后续表示指针空值时建议最好使用nullptr。

跟用0或者NULL表示指针空值的区别:

void f(int)
{
}

void f(int*)
{
}

f(0);//会调用第一个--默认情况下0被看作是一个整形常量,不行的话才是(void*)0
f(NULL);
//会调用第一个--NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量(具体看编译器是哪种)
f(nullptr);//会调用第二个

补充知识:像这种形参只给了类型的,没有给名字的也行,如果函数里面没有使用这个形参的话

作业部分

引用传值,指针传地址(X)
原因:引用表面好像是传值,其本质也是传地址

删除空指针是无害的,但是不能删除引用(对)
原因:空指针没有任何指向,删除无害,引用是别名,删除引用就删除真实对象
关于c++的inline关键字,以下说法正确的是(BC) 
A.使用inline关键字的函数会被编译器在调用处展开
B.头文件中可以包含inline函数的声明
C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数
D.递归函数也都可以成为inline函数

A:要看编译器有没有把他处理成内联函数
B:如果头文件里有内联函数的声明和定义的话就行
评论 44
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值