【C++】基础语法概念
目录:
一、命名空间
(一)namespace的价值
(二)namespace的定义
(三)命名空间的使用
二、函数重载
三、引用
(一)引用的概念
(二)引用的特征
(三)引用的使用
(四)const引用
(五)指针与引用的关系
一、命名空间
(一)namespace的价值
namespace的出现是为了优化C语言中命名冲突的问题。
比如在C中如果变量名、函数名一样,编译会报错,而C++祖师爷为了优化这一点就创造出了namespace。命名空间内可以定义变量/函数/结构体。只要我用了命名空间,即使我跟系统函数取同名或和做同一个项目的同事命名相同也不影响程序正常运行。
(二)namespace的定义
(1)定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。(见上图)
(2)命名空间必须定义在全局,不能在局部定义(如main函数内不可以!)
ps1:程序运行前需要经过预编译、编译、链接、再生成可执行程序。在编译过程中遇到变量,编译器查找的先后顺序是:先局部再库函数,所以下图第一个printf不可能打印出命名空间内定义的10,而是stdio.h中的函数rand。
ps2:要想打印出命名空间中rand值必须要遵循其访问规则。命名空间名::变量
::称为域作用限定符,可理解为加上才能限定在命名空间内只访问指定变量/函数/结构体,下图命名空间中定义的rand是整型,打印用%d。
因此,命名空间除了解决命名冲突问题外,也决定了数据的访问
(3)namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以rand不会冲突了。
(4)namespace可以多嵌套定义,既命名空间内再有命名空间。
(5)项⽬⼯程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。
(6)C++标准库都放在⼀个叫std(standard)的命名空间中。(后续用到标准库要先展开std)
(三)命名空间的使用
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。所以下⾯程序会编译报错。所以我们要使⽤命名空间中定义的变量/函数,有三种⽅式:
3.1 指定命名空间访问,项⽬中推荐这种⽅式。
命名空间名::变量 -------> bit::rand
3.2 using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。
using 命名空间名字::成员;-------> using byte::STPush;
3.3 展开命名空间中全部成员,项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便推荐使⽤。
using namespace 命名空间名字; -------> using namespace bit;
二、函数重载
在C语言中,不支持同一作用域出现同名函数,而C++允许,只要同名函数的参数个数不同或者参数类型不同即可。这样C++的调用就表现出了多态行为(既同一函数名可实现不同内容调用)
(一)以下函数都可构成重载:
所谓的自动识别参数其实是编译器通过参数类型或者个数不同调用了对应的函数重载,让我们以为调用的是同一个Add函数,其实不是。
(二)函数返回值类型不同不可构成重载:无法区分到底调用哪一个
(三)判断以下函数是否构成重载
可以。其符合重载函数要求中参数个数不同条件,只是调用的时候会产生重载和缺省参数分歧;比如f1()你无法判断调用哪个。
三、引用
(一)引用的概念
引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,语法层上编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间。比如你有小名、乳名,不管叫哪一个都知道是在叫你本人。
类型 & 引用别名 = 引用对象
下图:b、c、d都是a变量的别名,既然是别名就和引⽤的变量a共⽤同⼀块内存空间。通过验证,四个变量的地址都相同。
(二)引用的特征
(1)引⽤在定义时必须初始化 ,也就是只能给确切的引用对象取别名。
(2)⼀个变量可以有多个引⽤,例如一个人可以有多个外号。
(3)引用一旦引用一个实体,再不能引用其他实体。也就是b引用了a,b就不能再引用其他对象。
(三)引用的使用
(1)减少拷贝提高效率:
我们调用函数传参传的是值的话,形参是实参的一份临时拷贝。若实参空间大形参也会开辟大空间。传地址即使是减少了空间开辟但根据编译环境不同只要是指针就开辟4/8个字节大小空间。而引用别名不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间。
(2)改变引用对象同时改变被引用对象:(对象=变量)
对x、y取别名,其实和x、y是共用一块地址空间,改变rx和ry的值等于改变x和y的值
(3)可以对变量取别名同样也可以对指针取别名
(4)简化程序,避开指针
前提是你了解引用的情况下,不然反而会混乱!!
思考:结构体末尾LTNode和 * PNode表示什么意思?
LTNode:表示用typedef把整个结构体定义为LTNode。等于 :typedef struct ListNode LTNode;
* PNode:表示用typedef定义结构体指针,等于 :typedef struct ListNode* PNode;
(5)引用做返回值
以下是栈调用的一小部分代码,最后通过return返回一个值再对该值进行+10,结果编译器报左值错误。这其中涉及了一点寄存器的内容。(类型转换和返回值会涉及到寄存器/临时变量问题)
在我们看来return是把数据直接返回给函数,其实编译器是先用一个临时变量将这个数值先存储起来,真正返回的是这个临时变量,临时变量具有常性不能改变,因此对临时变量+10会报错。
类型转换也是通过寄存器/临时变量进行转换,再将其赋值给对应变量
返回值绝对不能返回局部变量,因为函数运行结束后空间会销毁,此时变量top已经不存在了,通过寄存器的值再访问回去是非法访问,绝对不行!!
有同学可能会疑问,那为什么这里引用就可以?
引用对被引用对象取别名并不开辟任何空间,对别名的更改等于对原数据的更改。
况且下面的代码rs是栈s的别名,栈和函数STTop不在同一块内存开辟空间,STTop运行结束销毁掉后不影响栈s以及它里面的内容,栈还是存在的。rs通过指针访问到栈中第四个数组元素4,rs.a[rs.top-1]语句是对具体元素4取别名,返回的是4的别名,所以STTO(s)接收到的是数组中4的别名,对别名+10直接影响4使它变为14。
引用只关注它返回的这块空间是否真实存在没有销毁,若是运行过程中销毁了则会报错
(四)const引用
被const修饰的变量不能改变原始值。
权限放大:若对这个变量取别名,那么就可能改变变量中原始值10,使其原本不可变的属性消失
权限平移:被const修饰的变量不能变,通过const引用(取别名)后其属性依然不变
权限缩小:普通变量b原本可以任意修改其值,被const引用(取别名)就不能再改变b的值,原本可变的属性消失
学了引用后不要和普通拷贝混淆了,判断其是不是引用(取别名)就是看它类型后面有没有 +&符
权限可以缩小不能放大!!
(五)指针与引用的关系
引用的出现是为了简化程序,但绝不是要替换指针。
C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有自己的特点,互相不可替代。
(1)语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
(2)引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
(3)引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
(4)引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。
(5)sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)
(6)指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。
PS:汇编指令层没有引用的概念,上层引用语法,汇编层用指针实现。
完