day2、C++对C语言的扩展

day2、C++对C语言的扩展

目录


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++在声明函数时可以设置占位参数。占位参数只有参数类型声明,而没有参数名声明。一般情况下,在函数体内部无法使用占位参数。

  • 占位参数也可以设置默认值,如int = 20

  • 占位参数也是参数,必须传值(如果没有设置默认值)

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语言的方式进行编译和链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值