【C++初阶】二、入门知识讲解(引用、内联函数、auto关键字、基于范围的for循环、指针空值nullptr)
温馨提示:这篇文章已超过435天没有更新,请注意相关的内容是否还可用!
=========================================================================
相关代码gitee自取:
C语言学习日记: 加油努力 (gitee.com)
=========================================================================
接上期:
【C++初阶】一、入门知识讲解
(C++关键字、命名空间、C++输入&输出、缺省参数、函数重载)-CSDN博客
=========================================================================
六 . 引用
(1). 引用的概念和特性:
引用的概念:
在C语言中,我们可以通过 指针 来实现对一个已存在的变量的“调用”,
但指针可能相对会比较难理解一些,
而在C++中,我们可以通过 引用 来实现对一个已存在的变量的“调用”,
通过一个已存在的变量(引用实体)可以设置一个 引用变量 ,
引用变量不是新定义一个变量,而是给已存在变量(引用实体)取了一个别名,
编译器不会为引用变量开辟内存空间,
引用变量和被它引用的变量(引用实体)共用一块内存空间。
(如:孙悟空,又叫弼马温,又叫齐天大圣,本质还是同一个人)
生成引用变量的方式:
- 引用变量类型& 引用变量名(对象名) = 引用实体
---------------------------------------------------------------------------------------------
引用的特性:
- 引用变量的类型必须和引用实体是同种类型的
- 引用变量在定义时必须初始化
- 一个变量可以有多个引用
(如:孙悟空被称为齐天大圣 ,而齐天大圣又可以被称为大圣)
- 一个引用变量一旦引用了一个实体(引用实体),
就不能再引用其它实体(引用实体),所以引用无法代替指针 - 因为引用变量和引用实体共用同块内存空间,
所以引用变量的改变也会导致实体变量的改变
图示:
(2). 引用的使用场景:
(重点)引用变量做函数的参数:
- 将函数的参数设置为引用变量,主函数调用函数时直接传变量名即可,
变量名传到函数这边后,函数中使用的就是该变量(引用实体)的引用变量,
又因为引用变量和引用实体共用同一块内存空间,
所以函数中引用变量(形参)的改变,也会导致引用实体(实参)的改变,
形参是实参的“别名”(引用变量),形参的改变就改变了实参
图示:
- 传引用做参数的效率比传值做参的效率高
图示 -- 传值做参和传引用做参的效率对比:
---------------------------------------------------------------------------------------------
(重点)引用变量做返回值:
- 将函数的返回值设置为引用变量,函数执行完成后,直接返回对应变量即可,
编译器在返回时会自动生成该返回变量的引用变量并返回(名字由编译器决定),
主函数中也要通过对应类型的引用变量来接收该函数返回的引用变量,
需要注意的是,函数中被返回的变量应该是静态变量,
这样可以保证该变量在函数结束后不会被销毁,引用变量作为该变量的“别名”,
共同使用同一块内存空间,该空间不会被销毁才能保证引用变量的值是准确的
- 还有一点,如果被返回的变量不是静态变量,
主函数第一次调用该函数并用对应引用变量接收时,其接收的值是不确定的,
接收结果取决于编译器在函数调用后会不会销毁对应栈帧,如果会销毁,则主函数中引用变量接收的值为随机值,
如果不会销毁,则接收的值为函数执行后的正常返回值,
(注:VS编译器在函数调用结束后不会销毁对应栈帧)
同时因为主函数中的引用变量和调用函数中的返回对象共用同一块内存空间,
所以在之后再次调用该函数且不接收其返回值的情况下,
主函数中的引用变量,即调用函数的返回对象的“别名”还会被改变,(不接收函数返回值但“别名”还是被改变了,这肯定不合适)
所以函数中被返回的变量应该是静态变量正确使用例子:
错误使用例子:
- 传引用返回的效率比传值返回的效率高
图示 -- 传值返回和传引用返回的效率对比:
- 传引用返回可以修改返回对象
图示:
(3). 常引用:
- 一个变量的权限分为可读可写、只读、只写,
一般创建一个变量后,该变量的权限为可读可写,
但如果对其进行const修饰附加常属性后,其权限就变成了只读,
即权限从可读可写变成了只读,该变量的权限缩小了,
而此时如果要对该变量进行引用,那么引用变量也需要被const修饰,
const引用变量 引用 const变量,成为const变量的“别名”,实现了权限的平移
-
引用变量实现权限的平移是被允许的,而引用变量实现权限的缩小也是被允许的,
即引用实体的权限是可读可写,而引用变量的权限是只读
-
引用变量实现权限的平移和缩小都是被允许的,唯独对权限的放大是不允许的,
即引用实体的权限是只读(被const修饰),
而引用变量的权限却是可读可写(未被const修饰)
- 补充:
当需要进行类型转换时:强制类型转换、隐式类型转换、类型截断,它都不是将右值转换为另一个类型然后就直接赋给左值的,
而是会生成一个临时变量,
先将转换后的右值赋给临时变量,再将此时的临时变量赋给左值。所以当你进行引用操作时,如果涉及到了类型转换,
引用实体会是类型转换过程中的临时变量,即给临时变量起了个“别名”,而不是对左值图示:
(4). 引用和指针的区别:
- 在语法层面(概念层面)上:
引用是定义一个变量的“别名”,而指针是存储一个变量的地址 - 引用在定义时必须初始化,而指针则没有要求
- 引用在初始化引用一个实体后,就不能再引用其它实体了,
而指针则可以在任何时候指向任何一个同类型实体
- 没有NULL引用(空引用),但有NULL指针(空指针)
- 引用和指针在sizeof中含义不同:
引用的结果为引用类型的大小,而指针的结果始终是地址空间所占字节个数(32位平台下为4个字节)
- 引用自加(++)即引用的实体增加1,
而指针自加(++)即指针向后偏移一个类型的大小
- 没有多级引用,但有多级指针
- 引用和指针访问实体的方式不同:
引用的话编译器会自己处理,而指针则需要显示解引用(*) - 引用比指针使用起来相对更安全(因为引用只能指定一个实体)
补充 -- 引用的底层实现:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
七 . 内联函数
(1). 内联函数的概念:
- 以inline修饰的函数叫做内联函数,
编译时C++编译器会在调用函数的地方展开该内联函数 - 内联函数没有函数调用建立栈帧的开销,所以内联函数可以提升程序运行的效率
- 内联函数克服了宏函数的缺点,
不用在意返回时括号的设置,还可以进行调试,
同时还有宏函数的优点,不用建立栈帧,
直接在调用函数的位置就会展开内联函数,提高效率现有一个宏函数:
使用内联函数替代宏函数:
(2). 内联函数的特性:
- inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,
在编译阶段,会用函数体替换函数调用
缺点:可能会使目标文件变大 ;优势:少了调用开销,提高程序运行效率 - inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,
一般建议:
对函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、
不是递归、调用频繁的函数采用inline修饰,
否则编译器会忽略inline特性,还是通过call指令通过函数地址调用函数 - inline不建议声明和定义分离,分离会导致链接错误,
因为内联函数的声明在头文件中被展开,会导致没有函数地址,
在链接时call指令就找不到函数实现(定义)的地址了
图示:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
八 . auto关键字(C++11)
(1). auto简介:
在早期C/C++中auto的含义是:使用auto修饰的变量,是自动存储器的局部变量。
在C++11中,标准委员会赋予了auto全新的含义:
auto不再是一个存储类指示符,而是作为一个新的类型指示符来指示编译器,
auto声明的变量由编译器在编译过程中推导得到,
定义一个auto类型的变量并对其进行初始化后,auto变量的类型可以被自动推导出来(2). auto的使用和使用注意事项:
auto的使用:
auto变量可以自动推导类型(初始化后),
auto变量的初始化对象可以是普通变量、指针变量和引用变量,auto变量除了有推导变量类型的作用,还可以起到省略类型的定义的作用,
因为C++中有些变量的类型是很复杂的,在理解了类型的情况下可以使用auto进行省略
图示 -- auto变量推导变量类型(不常用):
图示 -- auto变量省略类型的定义(常用):
---------------------------------------------------------------------------------------------
auto的使用注意事项:
- auto与指针、引用的结合使用:
用auto声明指针类型时,用auto或auto*都可以没有区别;
但用auto声明引用类型时则必须加&,即auto&
- 在同一行定义多个auto变量:
当在同一行声明多个auto变量时,这些auto变量的初始化变量必须是相同的类型,
否则编译器将会报错,因为编译器实际只对第一个初始化变量类型进行推导,
然后用推导出来的类型定义其它的初始化变量
- auto不能作为函数的参数(最好也不要做返回值);auto不能直接用来声明数组
- 为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
- auto在实际中最常用的优势用法是在C++11中提供的新式for循环中,
还有lambda表达式等进行配合使用
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
九 . 基于范围的for循环(C++11)
(1). 范围for循环的语法:
在C语言中,对数组的遍历操作可以通过数组下标或者指针的形式。
但对于一个本身就有范围的集合来说,还要程序员来说明循环的范围是有些多余的,
还有可能因此造成错误。因此C++11中引入了基于范围的for循环,
范围for循环后的括号通过冒号“ : ”分为两部分:第一部分为在范围内用于迭代的变量;第二部分则表示被迭代的范围
即:for(在范围内用于迭代的变量 : 被迭代的范围)
注:
与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环
图示:
(2). 范围for循环的使用条件:
- for循环迭代的范围必须是确定的:
对于数组而言,其范围就是数组中第一个元素和最后一个元素的范围;
对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围
错误示范:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
十 . 指针空值nullptr(C++11)
(1). C++98中的指针空值:
- 在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,
否则可能会出现不可预料的错误 - 比如未初始化的指针,如果一个指针没有合法的指向,
我们一般会这样对其进行初始化:
int* ptr1 = NULL;
这里的NULL实际是一个宏,在传统的C语言头文件中,
NULL可能被定义为字面常量0,或者被定义为无类型指针 (void*) 的常量0
- 在使用空值的指针时,都不可避免地会遇到一些麻烦,
当你想把NULL定义为无类型指针 (void*) 的常量时,NULL可能被编译器定义为常量0,因此与程序的初衷相悖
- 在C++98版本中,字面常量0既可以是一个整型数字,
也可以是无类型的指针(void*)常量,但是编译器默认情况下会将其看成是一个整型常量,
如果要将其按照指针方式来使用,还必须对其进行强转为 (void*)0,
显得头文件中的定义有些多余
- 所以在C++11版本中,有了一个新关键字:nullptr,来专门表示指针空值,
使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是一个关键字 - 在C++11中,sizeof(nullptr) 与 sizeof((void*) 0) 所占的字节数相同
- 为了提高代码的健全性,在未来的编程中表示指针空值时建议最好使用nullptr
图示:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C++入门知识讲解 -- 对应代码:
Test.cpp文件
#define _CRT_SECURE_NO_WARNINGS 1 #include #include /* * //C++ 兼容 C * //C语言版“hello world”: * int main() * { * printf("hello world\n"); * * return 0; * } */ //CPP: //兼容C语言 //面向对象 //泛型 //C的不足的弥补 /* //C++版“hello world”: #include //第一个特性:命名空间 using namespace std; int main() { //第二个特性:IO流 cout
- 在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,
- for循环迭代的范围必须是确定的:
- auto与指针、引用的结合使用:
- inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,
- 以inline修饰的函数叫做内联函数,
- 在语法层面(概念层面)上:
- 一个变量的权限分为可读可写、只读、只写,
- 传引用返回可以修改返回对象
- 传引用返回的效率比传值返回的效率高
- 将函数的返回值设置为引用变量,函数执行完成后,直接返回对应变量即可,
- 传引用做参数的效率比传值做参的效率高
- 将函数的参数设置为引用变量,主函数调用函数时直接传变量名即可,
- 引用变量的类型必须和引用实体是同种类型的


















