c++基础--持续更新ing

基本语言 + 类和数据抽象
1,基本语言
(1)const的作用有哪些,谈一谈你对const的理解?
什么是const?
Const的本质是常量,对于一个在作用域内不需要改变的值,可以把它设为常量。常量在被赋值后就无法被改变了。
为什么要引入const
取代预编译指令,消除它的缺点,继承优点。
Const几种常见类型
① 定义常量,常量在定义域内值不可变
② 修饰常数组
③ 修饰常对象
④ 修饰常指针
⑤ 修饰入参,函数,类的成员函数。
⑥ 在另一个文件中使用const
(2)描述char、const char、char* const、const char* const的区别?**
char*:指针
const char*:常量指针,地址中的内容无法改变
char* const:指针常量,指针指向的内存地址无法被改变
const char* const:常量指针+指针常量,内存和数据都无法被改变。
(3)指针常量和常量指针有什么区别?
常量指针:指向常量的指针。顾名思义是一个指针指向的内存,内存中的数据是不能修改的。
const int * p; int const * p;
指针常量:指针指向的内存是不能改变的,但是内存中的内容是可以改变的。指针常量必须在声明的时候就进行初始化。因此指针常量不允许先声明一个常量再对其进行赋值,就像声明一个常量一样。
int * const p=&a;
https://blog.csdn.net/jackystudio/article/details/11519817
(4)static的作用是什么,什么情况下用到static?
Static的存在意义
创建独立于具体对象的变量或函数。–即使没有对象,也能调用属性和方法。
形成静态代码块以优化程序性能。类在加载的时候执行一次
Static的独特之处
被类的实例对象所共享,在类加载的时候就要分配空间,后续可任意赋值。所以与实例变量相比,静态变量内存只有一份。
应用场景
成员变量
成员方法(静态内部类实现单例模式)
静态代码块
静态类(只能修饰内部类)
(5)全局变量与局部变量
全局变量和局部变量的区别,是怎样实现的,操作系统和编译器是怎么知道的
 生命周期不同。全局变量随主程序创建和创建,随主程序销毁而销毁;局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在;
 使用方式不同:通过声明后全局变量程序的各个部分都可以用到;局部变量只能在局部使用;分配在栈区。
 操作系统和编译器通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行的时候被加载。局部变量则分配在堆栈里面 。
(6)内存对齐的概念?为什么会有内存对齐?
https://zhuanlan.zhihu.com/p/30007037
计算机字节是按照byte划分的,但是处理器是按照2/4/8/16 字节为单位来读取内存。比如4字节的处理器只能从地址为4的的倍数的内存开始读取数据,假如没有内存对齐机制,数据任意存放,一个int类型的变量从地址1开始存放,变量存放在1,2,3,4。但是处理器只会从0开始读取,然后从4,开始读取5,6,7.最后留下两块数据存在寄存器中,效率低下。
现在有内存对齐,int类型数据只能存放在按照对齐规则的内存中。所以处理一下就可以一次性取出数据,无需额外操作。
常见的内存对齐:param(4)
(7) inline 内联函数的特点有哪些?它的优缺点是什么?
Q:为什么要引入内联函数?
A:解决频繁调用的小函数大量消耗栈空间的问题。
栈空间:放置程序的局部数据(函数内数据)的内存空间,系统中,栈空间是有限的,频繁使用会造成栈空间不足,导致程序出错。如,函数的死循环递归问题。
Q:inline使用限制
A:只适用于体积小代码简单的函数,不能包含复杂的语句,内联是以代码复制为代价的,仅仅省去了函数调用的开销,来提高函数的执行效率。
Q:常见的建议。
内联只是对编译器的建议,真正内联看实际情况,建议内联放在头文件中,编译器必须可见内联函数的定义。类中的成员函数默认为内联。
(8) extern有什么作用,extern C有什么作用?
 作用
声明extern关键字和函数表示他们可以跨文件访问,全局函数是可以自动省略extern的。Extern“c”,一方面是extern,另一方面表示修饰的目标是“C”。
 用法
 与static的关系
水火不容,static表示的限定函数或者变量的范围,extern表示的是可以被其他对象使用。但在存储方面,static和对象本身是分开的,extern也是分开存储的。Extern可以多次声明,但只能定义一次。
 与const的关系
 内存的分配
(9)struct和class有什么区别?C和C++中的struct的区别?
Struct能有成员函数,能继承,能实现多态,和class的最大的区别就是默认的访问控制。Struct默认是public,class默认是private。

C中struct是用户自定义数据类型,C++中,struct是抽象数据类型,支持成员函数的定义,C++中,struct的成员默认访问说明符为public,class为private,C中的struct是没有权限设置的,C++中struct增加了访问权限,且可以和类一样有成员函数。C++中的struct等于class,有变量,构造函数,虚函数,有继承和多态,只是class默认成员权限是private,而struct默认成员权限是public,
C++中的struct和class的区别
继承权限:struct默认是public继承,class默认是private继承
访问权限:struct默认是public继承,class默认是private访问
(10)函数重载,重写(覆盖)有什么区别?

重载不是面向对象的特征之一,重载C语言中也有,是函数名称相同,但是形参的个数,数据类型或者顺序不一样,对函数的调用,在编译器就已经确定了,是静态的,他们的地址在编译期就已经确定了。
重写和多态相关,当子类重新定义父类的虚函数后,父类指针动态调用属于子类的该函数,这样的函数调用在编译期间是无法确定的,是在运行期绑定的。
重定义(隐藏):在继承关系中,子类实现了一个和父类名字一样的函数(只关注函数名,和参数与返回值无关)
(11)多态,虚函数,运行时多态的理解
多态:静态多态+动态多态。静态多态主要是重载,在编译的时候就已经确定了。动态多态是利用虚函数实现的,在运行期间动态绑定。比如基类指针指向子类对象时,使用基类的指针的去调用了子类中重写了父类的虚函数的时候,会调用子类重写后的函数。
虚函数:在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数。
虚函数表:在有虚函数的类中,C++的最开始的部分是在一个虚函数表的指针,指针指向一个虚函数表,表中放有虚函数的地址,实际的虚函数在代码中。子类会继承虚函数表,当子类重写父类的虚函数时,会将继承到的虚函数表中的地址替换为重写的函数地址。虚函数完成了多态。

对虚函数机制的理解,单继承、多继承、虚继承条件下虚函数表的结构
如果虚函数是有效的,那为什么不把类中所有函数设为虚函数?
(1) 哪些函数是不能设成虚函数的
构造函数,不能被继承的。构造函数时,实例还没有被初始化,无法通过虚函数表指针寻找虚函数。
内联成员函数:内联要求在编译时展开该函数,而虚函数要求动态绑定。
静态成员函数:不属于任何一个类,被类的成员对象共享,不可继承。
友员函数:不属于成员函数,不可继承。
(2) 虚函数开销大(如何大??待定)
(3) Virtual本身对程序来说起到的是提醒作用
构造函数可以是虚函数吗?析构函数可以是虚函数吗?
构造函数就是为了在编译阶段确定对象的类型以及为对象分配空间,如果类中有虚函数,那就会在构造函数中初始化虚函数表,虚函数的执行却需要依赖虚函数表。如果构造函数是虚函数,那它就需要依赖虚函数表才可执行,而只有在构造函数中才会初始化虚函数表,鸡生蛋蛋生鸡的问题,很矛盾,所以构造函数不能是虚函数。

基类析构函数是可以设成虚函数的。一般基类的析构函数都要设置成虚函数,因为如果不设置成虚函数,在析构的过程中只会调用到基类的析构函数而不会调用到子类的析构函数,可能会产生内存泄漏。

纯虚函数的作用是什么?什么场景需要用到纯虚函数?虚函数和纯虚函数的区别?
(1)为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
(2)在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
静态函数和虚函数的区别
静态函数在编译的时候已经确定时机,虚函数在运行的时候动态绑定,虚函数因为用了虚函数表机制,调用的时候会增肌一次内存开销。

(12)类的大小怎么计算?

规律:
1, 空类,单一继承的空类,多成继承的空类。1个字节
2, 一个类中,虚函数本身、成员函数(静态和非静态)静态数据成员都不占类对象的存储空间。
3, 一个对象的大小≥非静态成员大小的综合。
4, 当类中有虚函数,在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数VTable。
5, 虚继承:虚继承的实现是通过虚函数表,会同时增加一个或多个vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,两者所占空间为8.
6, 类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(13)volatile关键字的作用?什么时候需要使用volatile关键字
volatile是“易变的”、“不稳定”的意思。当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,而不是从寄存器中读取备份, 读取的数据立刻被寄存。例如:
在这里插入图片描述

1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。

1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。

(14)了解各种强制类型转换的原理及使用?
在这里插入图片描述
(15)指针和引用有什么区别?什么情况下用指针,什么情况下用引用?
在这里插入图片描述
(16)malloc的内存可以用delete释放吗?malloc出来20字节内存,为什么free不需要传入20呢,不会产生内存泄漏吗?
不可以,必须成对使用。

free()释放的是指针指向的内存,不是指针!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了。

(17)宏定义的作用是什么?
宏定义的有效范围是当前文件内有效。一种是在头文件内,一种是在元定义中。
(18)typedef和define有什么区别?
https://www.runoob.com/note/24230
(1)#define可以使用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样。
(2)在连续定义几个变量的时候,typedef能够保证定义的所有变量均为同一类型,而#define则无法保证。
(19)零长数组
零长数组是为了满足变长数组。一般在结构体的最后一个,申请一个长度为0的数组,使得结构体本身是可变长的,对于编译器来说,结构体本身是不占据内从空间的,它只是一个偏移量。数组名这个符号本身代表了一个不可修改的地址常量,就像绳子的一头被绑定,绳子的长度是可变的。缺点是必须在结构体最后一位进行构造,使用上有限制。
(20) 什么是野指针,以及如何避免野指针?
野指针,也就是指向不可用内存区域的指针。通常对这种指针进行操作的话,将会使程序发生不可预知的错误。
“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。野指针的成因主要有两种:
(21)析构函数的作用
析构函数和构造函数相对应,当对象结束生命周期时,系统会自动执行析构函数。
~Base() // 无返回值,无入参
如果类中有一个指针,且在使用的过程中动态的申请了内存,最好显示构造析构函数,释放申请的内存空间,避免内存泄露。
类析构顺序:派生类本身析构函数->对象成员析构函数->基类析构函数

野指针:被释放的内存
指针声明时,要初始化
指针使用时,要判空
指针不适用的时,记得释放
尽量使用引用代替指针。
**
(22) ++i和i++的实现
在这里插入图片描述

(23)c++函数栈空间的最大值
默认是1M,不过可以调整。
3,编
译和底层**
(1) c++源文件从文本到可执行文件经历的过程
四个过程。

  • 预处理阶段:对源代码文件中文件包含关系(头文件),预编译语句(宏定义)进行分析和替换,生成预编译文件
  • 编译阶段:预编译文件->特定汇编代码,形成汇编文件
  • 汇编阶段:汇编文件-》机器代码,形成可重定位目标文件
  • 链接阶段:多个目标文件和所需要的库最终形成可执行目标文件

(2)include头文件的顺序以及双引号和<>的区别
编译器预处理阶段查找头文件的路径不一样。
“”: 当前头文件-》编译器设置的头文件-》系统变量CPLUS_INCLUE_PATH/C_INCLUE_PATH 指定的头文件
<>:编译器设置的头文件-》系统变量CPLUS_INCLUE_PATH/C_INCLUE_PATH 指定的头文件

(3)C++内存管理,内存分配,内存泄露(什么是+怎样处理)

1. 编译程序占用内存
**栈区(stack):**由编译器自动分配与释放,存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。
堆(heap):程序员显示释放,没有释放的话,程序结束时可能有OS回收(???),分配类似于链表。
全局区(static):存放全局变量,静态数据,常量。程序结束后系统自动释放,全局区分为已初始化全局区data)和未初始化全局区(bss)。
**常量区(文字常量区):**存放常量字符串,程序结束后有系统释放。
**代码区:**存放函数体(类成员函数和全局区)的二进制代码。
2. 三种内存分配方式
**从静态存储区分配:**内存在程序编译的时候已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

**在栈上创建:**在执行函数时,函数内局部变量的存储单元可以在栈上创建,函数执行结束时,这些内存单元会自动被释放。
栈内存分配运算内置于处理器的指令集,效率高,但是分配的内存容量有限。

**在堆上分配:**malloc/new,这种任意申请的内存,程序员有责任显示释放。很多内存泄露都出现在这里。

3. 内存分配图
在这里插入图片描述
4. 高频考点
动态内存管理:

堆栈区别:

**空间大小不同。**栈空间娇小,VC6默认1M,堆最大可以达到4G。
**管理方式不同。**栈是编译器自动申请/释放,堆需要程序员手动申请和释放
**能否产生碎片。**栈是不会产生碎片的,因为stack在使用一个元素之前,上一个已经弹出了。但不停的调用malloc和free会造成碎片。
分配方式不同。堆都是动态分配,栈有静态+动态。静态是编译器完成,如局部变量。动态是malloc,但是栈和堆的效率不同,栈是编译器自动释放,无需人工显示实现。
支持效率不同。栈效率远高于堆。栈是机器系统提供的数据结构,计算机底层提供栈的支持,分配专门的寄存器来存放栈的地址,压栈出栈都有相应的指令。堆是库函数提供的,机制比较复杂,库函数根据一定的算法进行内存搜索,比较慢。

静态全局变量,全局变量,静态局部变量,局部变量:

  • 面向对象和泛型编程。单独整一张
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值