1. C++关键字
2. 命名空间
2.1 命名空间定义
存在多个相同的命名空间的时候,编译器编译的时候会把他们合并,如下面
命名空间可以嵌套
PS:命名空间定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中,但是成员的生命周期没有改变,仍然是全局的。
2.2 命名空间使用
3. C++输入&输出
4. 缺省参数
4.1 缺省参数概念
4.2 缺省参数分类
5. 函数重载
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。
比如:有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”
5.1 函数重载概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。
下面都是函数重载:
下面这两个不是函数重载!
5.2 名字修饰
那么为什么C++支持函数重载而C语言不支持呢?
一个程序要运行起来要经历以下几步:假设有以下文件
f.h f.cpp test.cpp
1.预处理——头文件展开,宏替换,条件编译,去掉注释
生成f.i test.i
2.编译——检查语法,生成汇编代码
生成f.s test.s
3.汇编——把汇编代码转换成二进制的机器码(让CPU能看懂)
生成f.o test.o
4.链接——找调用函数的定义地址,链接对应上,合并到一起
生成a.out可执行程序
在链接阶段,编译器看到test.o调用了哪些函数,就会去f.o的符号表中找对应函数的地址,然后链接在一起,那么要怎么去找呢?C++和C语言采用了不同的名字修饰,就会用不同的名字去找
5.3 extern “C”
再新开一个项目,调用这个库,这里对新项目的属性进行更改。
然后来看一下现在能否调用
6. 引用
6.1 引用概念
6.2 引用特性
6.3 常引用
这里我们取别名的规则是:
对原引用变量,权限只能缩小,不能放大。这里权限指的是读写权限。
因此对const常量只能进行读,也就只能用const引用。
在下面将double赋给int别名的过程中,编译器会产生“临时变量”,将double d中整数部分赋给临时变量, 这个临时变量再将这个整数赋给另一个int类型的临时变量,最后int把临时变量赋给rd,也就是说rd引用的其实是这个int临时变量,而临时变量具有常属性,因此要用const引用。
6.4 使用场景
n和ret地址一样
这里n不再是静态变量了,出了函数作用域后空间返还,而我们的ret是n的别名,这是不是就相当于野指针。因此只有第一次调用函数后ret中的值是1,调用一次函数(函数重载<<)后n的空间被制成随机数,因此剩下两次都是随机数,因此我们用引用作返回值的时候返回的应该是出了函数定义域还存在的变量的引用,否则就应该按值返回,这里和指针是一个道理。
明白了这些下面这个程序的结果就应该很明确了,应该是7
6.5 传值、传引用效率比较
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
这里用程序来比较一下
不是很明显的原因是我们计算机的速度实在太快,但是还是有效率差异的。
指针和引用的效率是一样的,因为引用的底层和指针的底层是一样的!
6.6 引用和指针的区别
从语法概念上:引用是别名,没有额外开空间,而指针存储变量的地址,开辟了4/8字节的空间
从底层实现的角度:引用实际上是有空间的,因为引用是按指针的方式来实现的
来看一下指针和引用的汇编
汇编指令完全一样,也印证了底层是一样的
那引用和指针的不同点是什么呢?
1. 引用在定义时必须初始化,指针没有要求
2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
3. 没有NULL引用,但有NULL指针
4. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
6. 有多级指针,但是没有多级引用
7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
8. 引用比指针使用起来相对更安全
7.1 概念
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
在学习C语言的时候,我们遇到函数体短小且频繁调用的函数的时候,采用的优化方式是使用宏进行替换,但是宏太繁琐啦,难以看懂,因此c++就产生了内联函数。
那么内联函数是如何进行优化的呢?来看代码。
当我们不用inline定义函数的时候,函数是怎么调用的呢?
进行函数压栈,然后用call指令调用。那么用了inline后呢
我们发现在汇编代码中直接对函数调用的代码进行了替换,没有压栈和call调用指令。
7.1 特性
1. inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
2. inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
例如如果在.h和.cpp文件中分别声明和定义内联函数,那么在链接的时候就会出现错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用
那么内联函数相比宏有哪些优点?
1.内联函数在debug版本下支持调试,而宏不支持
2.内联函数就是普通函数的写法,解决了宏晦涩难懂的问题
8. auto关键字(C++11)
8.1 auto简介
在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
8.2 auto的使用细则
8.3 auto不能推导的场景
9. 基于范围的for循环(C++11)
9.1 范围for的语法
9.2 范围for的使用条件
10. 指针空值nullptr(C++11)
10.1 C++98中的指针空值
在传统头文件中有如上定义,我们发现在C++中Null被宏替换为0,而如果我们要对一个指针赋空值,仍采用NULL的话有些不妥,因为0毕竟是一个数字,而不是指针类型,因此C++11引入了nullptr,其实也就相当于(void*)0。
今日份分享到此结束,祝小伙伴们周末愉快!
发表评论