day2、C++对C语言的扩展
目录
- day2、C++对C语言的扩展
- 1、::作用域运算符
- 2、名字控制
- 3、全局变量检测增强
- 4、C++中所有的变量和函数都必须有类型
- 5、更严格的类型转换
- 6、struct类型加强
- 7、新增bool类型关键字
- 8、三目运算符功能增强
- 9、C/C++中的const
- 10、引用(reference)
- 11、内联函数
- 12、函数的默认参数
- 13、函数的占位参数
- 14、函数重载(overload)
1、::作用域运算符
通常两个同名变量,一个全局变量,一个局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
2、名字控制
C语言可以通过static关键字来使得名字只得在本编译单元内可见,在C++中我们可以通过一种通过命名空间来控制对名字的访问。
2.1C++命名空间(namespace)
大规模程序设计中容易导致命名发生冲突,标准C++引入关键字namespace可以更好的控制标识符的作用域。
2.2命名空间使用语法
-
创建一个命名空间
-
命名空间只能全局范围内定义,不能在函数内部定义命名空间
-
命名空间可以嵌套命名空间
-
命名空间是开放的,可以随时把新成员加入已有的命名空间中
-
命名空间中声明的函数可以和实现分离
-
无名命名空间,意味着命名空间中的标识符只能在本文件内访问,相当于给这个标识符加上了static,使得其可以作为内部链接
-
命名空间可以起别名
2.3using声明
using声明可以使得指定的标识符可用
如果命名空间中包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合
2.4using编译指令
using编译指令使得整个命名空间标识符可用。使用using声明或者using编译指令会增加命名冲突 的可能性。使用作用域运算符则不会出现二义性。
2.5命名空间使用
当引入一个全局的using编译指令时,就为该文件打开了该命名空间;如果产生命名冲突,就需要通过明确的限定或者选择性的使用using声明来消除名字冲突。
3、全局变量检测增强
C++中对于全局变量的检测更加严格。
4、C++中所有的变量和函数都必须有类型
C语言中变量没有给定类型时,表示可以是任意类型,但C++必须要指定类型。
5、更严格的类型转换
C语言中赋值时会自动识别,但是C++中不同类型的变量一般是不能直接赋值的,需要手动添加强转。
6、struct类型加强
6.1C语言中定义结构题变量时需要加上struct关键字,C++不需要
6.2C语言中结构体只能定义成员变量,不能定义成员函数;C++都可以
7、新增bool类型关键字
bool类型有两种内建的常量,true(转换为整数为1),false(转换为整数为0)。
7.1bool类型只有两个值,true false
7.2bool类型占一个字节大小
7.3给bool类型赋值时,非0值会自动转换为true,0值会自动转换为false
8、三目运算符功能增强
8.1C语言三目运算符表达式返回值为数据值,为右值,不能赋值
8.2C++三目运算符表达式返回值为变量本身(引用),为左值,可以赋值
9、C/C++中的const
9.1const概述
用来限定一个变量不允许改变,它将一个对象转换成一个常量。
9.2C语言中的const
C 语言中const是一个全局只读变量,会分配内存,const修饰的只读变量是外部连接的。不能用const修饰的变量去定义数组长度。
9.3C++中的const
在C++中,一个const不必创建内存空间。一般来说,是否为const常量分配内存空间依赖于如何使用,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define),那么该存储空间就不必创建;当取一个const地址,或者把它定义为extern,或者用变量初始化const变量,或者自定义数据类型加const,都会为const创建内存空间。
在C++中,出现在所有函数之外的const作用于整个文件(在文件外不可见),默认为内部连接。C++中其他的标识符一般默认为外部连接。
9.4C/C++中const异同总结
C语言全局const会被存储到只读数据段;C++中全局const当声明extern或者对变量取地址时,编译器会分配地址,变量也是存储在只读数据段。都不可修改。
C语言中局部const存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值
C++中局部const变量
1.对于基础数据类型,也就是const int a = 10这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存。可以通过指针操作分配的空间来改变值。
2.对于基础数据类型,如果用一个变量初始化const变量,如果const int a = b,那么也是会给a分配内存。
3.对于自定义数据类型,比如类对象,那么也会分配内存。可以通过指针间接赋值修改对象。
9.5尽量用const替换#define
用#define定义的宏从未被编译器看到过,因为在预处理阶段,所有的该变量都被替换成了定义的值,可能会导致一些问题,用const变量替换宏会更好一点。
1.const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查。
2.const有作用域,而#define不重视作用域,默认定义处到文件结尾。
10、引用(reference)
10.1引用基本用法
变量名实质是一段连续内存空间的别名,是一个标号(门牌号);程序中通过变量来申请并命名内存空间;通过变量的名字可以使用存储空间。引用其实就是作为一个已定义变量的别名。
int &a = b;
给变量a取一个别名b,操作b就相当于操作a,一个变量可以有无数各别名,且大家地址都是同一个。
-
类型标识符是指目标变量的类型
-
必须在声明引用变量时进行初始化
-
引用初始化后不能改变
-
不能有null引用。必须确保引用是和一块合法的存储单元关联。
-
可以建立对数组的引用
int arr[10];
int (&arr1)[10] = arr;
10.2函数中的引用
当引用被用作函数参数时,在函数内对任何引用的修改,将对函数外的参数产生改变;从函数中返回一个引用,必须像从函数中返回一个指针一样对待。当函数返回值时,引用关联的内存一定要存在。
10.3引用的本质
引用的本质在C++内部实现是一个指针常量
int& b = a;等价于int* const b = &a;
指向不能变(指针变量b由const约束不能改变等于a的地址),只能指向a的地址&a,但是指向该地址的值可以通过*b来改变。引用所占用的空间大小和指针相同。
10.4指针引用
当创建的对象为一个指针时,函数参数变成指针的引用,就用不着取指针的地址。
10.5常量指针
-
字面量不能赋值给引用,但是可以赋值给const引用;
int &ref = 10;是不可以的
const int &ref = 10;可以,等价于int tmp = 10;const int &ref = tmp;通过创建临时内存的方式实现来引用。 -
const修饰的引用不能修改。
-
将函数的形参定义为常量引用的好处:
- 1.引用不产生新的变量,减少形参与实参传递时的开销;
- 2.由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
11、内联函数
11.1内联函数的引出
- 在C语言中经常把一些短且执行频繁的计算写成宏,而不是函数,这样可以提高效率,宏可以避免函数调用的开销,这些都由预处理器来完成。
-
C++中预处理宏存在两个问题
- 1.在C语言中存在,宏看起来像一个函数调用,但有时会隐藏一些难以发现的错误;
- 2.在C++中,预处理器不允许访问类的成员,也就是说预处理器不能用作类的成员函数。即类中的成员函数无法通过预处理器提前编译。
-
为了保证预处理宏的效率又增加安全性,而且还能像一般成员函数那样可以在类里访问自如,C++引入类内联函数
- 内联函数为了继承宏函数的效率,没有函数调用开销,然后又可以像普通函数那样,可以进行参数,返回值类型的安全检查,又可以作为成员函数。
11.2预处理宏的缺陷
- 在使用宏时,只是做预处理器符号表中的简单替换,虽然提高类效率,但也因此不能进行参数有效性检测;
- 宏时全局作用域,不能作为类成员函数;
11.3内联函数基本概念
内联函数具有普通函数的所有行为,唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。
- 在普通函数(非成员函数)前面加上inline关键字使之成为内联函数。必须注意必须函数体和声明结合在一起,否则编译器将它当作普通函数来对待。inline void func(int a);只有函数声明,不是内联函数
- 以空间换时间
11.4类内部的内联函数
在类内部定义内联函数时不是必须加inline的,任何类内部定义的函数自动成为内联函数。
11.5内联函数和编译器
对于任何类型的函数,编译器会将函数类型(包括函数名,参数类型,返回值类型)放入符号表中;同样对于内联函数,当没有发现错误时,也会将内联函数放入符号表。
以下情况不考虑将函数进行内联编译
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 不能对函数进行取址操作
内联仅仅只是给编译器一个建议,编译器不一定会接受这个建议。如果没有将函数声明为内联函数,编译器也有可能将此函数做内联函数编译;一个好的编译器将会内联小的、简单的函数。
12、函数的默认参数
C++在声明函数原型的时候可以为一个或多个参数指定默认值,当函数调用的时候没有指定这个值,编译器会自动采用默认值。
13、函数的占位参数
C++在声明函数时可以设置占位参数。占位参数只有参数类型声明,而没有参数名声明。一般情况下,在函数体内部无法使用占位参数。
14、函数重载(overload)
14.1函数重载概述
在传统C语言中,函数名必须时唯一的,程序中不允许出现同名的函数;但是在C++中允许出现同名的函数,这种现象称为函数重载。
14.2函数重载基本语法
实现函数重载的条件
- 同一个作用域
- 参数个数不同
- 参数类型不同
- 参数顺序不同
const引用也可以作为重载的条件
返回值类型不作为函数重载的依据
函数重载和默认参数一起使用时需要注意二义性问题的产生
14.3函数重载实现原理
编译器用不同的参数类型来修饰不同的函数名
14.4extern "C"浅析
由于C++中需要支持函数重载,所以C和C++中对同一个函数经过编译后生成的函数名是不相同的。如果在C++中调用一个使用C语言编写模块中的一个函数,编译器会按照编译C++的方式去编译C语言程序,会导致错误。解决方式就是加入extern “C”。
extern "C"的主要作用就是为了实现C++代码能够调用其他C语言代码。加上extern "C"后,这部分代码编译器会按照C语言的方式进行编译和链接。