C++面经
1. 讲讲main函数执行之前与执行之后发生了啥?
执行前,初始化全局变量与静态变量,设置栈指针,为main函数的参数赋值。、
执行后,调用析构函数对全局变量,静态变量进行析构
2. 讲讲结构体内存对齐问题
内存对齐通过解决内存分配来优化系统读取的速度,比如int类型占4个字节,char占一个字节,定义一个结构体struct mystruct{int a; char b;}默认这个结构体占8个字节,使用#pragma pack(n)来指定字节对齐方式。如#pragma pack(4) struct mst1{char a; short int b; int c;}mst1结构体会占据8个字节 产生1个字节的碎片,#pragma pack(1) 会占据7个字节,没有碎片, #pragma pack(2) 会占据8个字节,1个字节的碎片。
3. 指针和引用的区别
(1) 指针是一个指向变量的地址。引用是一个变量的别名
(2) 指针初始化可以为空,而引用初始化必须指定。
(3) sizeof(指针)返回的是指针大小,sizeof(引用)返回引用变量的大小
(4) 当函数参数中用到指针时需要对指针进行一个复制,而使用引用则不需要,同时在函数中如果是对指针进行修改的话不影响到指针指向的变量,而对引用修改的话会修改引用指向的变量。
(5) 指针可以多级指向,引用只能指向一个。
(6) 引用一旦初始化后,它指向的地址就不会再发生改变,指针不一样,可以改变指针的指向。
4. 什么时候函数传递参数时使用指针,使用引用?
(1) 当要用到参数返回需要指针时,使用指针
(2) 当对栈空间比较敏感(递归)使用引用,因为不需要复制的操作,减少对内存的使用
(3) 类对象进行参数传递的时候使用引用
5. 堆和栈的区别
(1) 申请与释放方式不同
(2) 堆较慢,栈较快
(3) 堆申请内存大小与栈申请大小不一致。
6. 为什么栈比堆块
(1) 栈由系统进行分配与释放,有专门的寄存器储存栈的内存地址,并且系统有专门的指令进行对栈的操作,而堆由程序员自己分配与释放,分配时候会根据堆空间的大小根据某种算法去找寻适合的空间进行分配,且因为堆中存放的是变量的指针,所以我们进行读取时要读取两次。
7. new/delete 与 malloc/free的异同
(1) New/delete malloc/free都是对堆上内存的分配与释放
(2) New 是类型安全的,在编译阶段就需要进行类型的检验, malloc类型不安全,是void*类型,需要强制类型转换
(3) new/delete 是运算符,可以通过opertor new 实现重载, malloc/free 是库函数,不能重载
(4) New 无需指定内存分配大小,malloc需指定
(5) New/Delete会调用构造函数及析构函数,malloc/free只是对内存进行分配与释放
8. new 和 delete如何实现
(1) new是一个运算符,通过调用operator new库函数实现对指定类型的内存分配,同时调用构造函数完成初始化,最后返回一个该类型的指针
(2) delete先调用指向对象的析构函数,然后调用operator delete库函数实现对内存的释放
9. 有了malloc与free,为什么还要有new/delete?
(1) malloc/free是库函数,只包含了对内存的释放及分配,对于非基本数据类型对象,我们需要执行构造函数及析构函数,malloc/free无法实现,而new/delete是运算符,可以重载成不同的方法。所以用new/delete可以实现不同类型之间特有的初始化及释放方式。
(2) 不用再担心类型之间的转换。
10. 被free回收的内存是立即返回到操作系统吗?
(1) 不是的,free回收的内存会放在内存池中,等到下一次有人申请内存时再查看是否满足,这样就减少了系统调用的次数。同时会有一种指定的算法整合小内存减少内存碎片的问题。
11. 宏定义与函数有什么区别
(1) 宏定义发生在预处理阶段
(2) 宏定义参数不需要指定类型,函数则需要
(3) 宏定义没有分号
(4) 宏定义相当于在代码中插入文字,不需要调用,而函数需要调用因此速度没有宏定义块
12. typedef与宏定义有什么区别?
(1) typedef发生在编译阶段,需要进行类型检查
(2) 宏定义发生在预处理阶段,不需要进行类型检查
(3) Typedef 定义指针时与宏定义指针区别很大,如typedef t_char char*, #define d_char char* t_char a,b; a,b均是指针类型,而 d_char c,d; c是指针类型,而d是char类型
(4) typedef需要加分号
13. 变量声明和定义区别
(1) 声明无需分配空间,定义则需要为变量分配空间
(2) 声明可以通过extern的方式进行多次声明,而定义只能在一处进行定义
14. strlen与sizeof区别?
(1) Strlen()是库函数,参数必须是字符串指针,且结尾必须为’\0’
(2) Sizeof()是运算符,编译期间就可以计算
(3) Sizeof()因为在编译阶段就要计算,所以不能计算动态内存
15. 一个指针占多少个字节?
(1) 根据不同的操作系统而言,我笔记本上是占4个字节,可以通过sizeof(指针)查看
16. 常量指针和指针常量的区别?
(1) 指针常量:const int* a;a可以改变指向, *a不可以改变指向
(2) 常量指针:int* const a;a不可以改变指向,*a可以改变指向
17. C++和python区别
(1) Python是一门解释性语言,不需要进行编译等操作,速度较慢
(2) C++是一门编译性语言,需要编译,因此执行起来较快
(3) Python缩进是通过空格的方式,C++则是使用{}
(4) C++需要指定变量的类型,python则不需要
2022.11.9
1. C++和JAVA区别
(1) 语言层面
① C++中数组是使用指针构造的伪数组,可以实现自增自减的操作,因此也要考虑到数组越界导致非法访问的问题,java数组越界则会直接报错
② C++使用抽象实现多态,java还可以通过接口实现
③ Java它的可移植性强,而c++移植性较差,比如可能在windows上采取的是小端存储的方式,到了unix则变为大端存储,这样我们需要对其重新进行编码,而java只要是有一个虚拟机就可以实现一次编码,多处运行。
(2) 内存方面
① C++需要考虑内存泄漏的问题,java有一套自己的垃圾回收机制
② Java没有指针,内存分配释放由系统进行,不需要考虑内存碎片的问题
(3) 应用层面
① C++因为直接预处理编译汇编然后链接生成一个exe程序,因此很适合做桌面程序
② JAVA在互联网有着远超C++的优势,有着许多成熟的框架
2. C++中struct 与 class的区别
(1) 成员未标明可见的情况下,struct默认public,class默认private
(2) 未指明指定方式继承的情况下,struct默认public继承,class默认private继承
3. define宏定义与const的区别
(1) 宏定义发生在预处理阶段,不需要指定类型,const发生在编译阶段,需要类型并检查类型
(2) 宏定义只是代码的替换,不需要占据空间,而const修饰成员时需要为其分配内存
4. const 与 static 作用
(1) 不考虑类:
① const变量声明时必须初始化,并且以后不能被修改
② const形参可以由const或非const变量作为参数,反之则不行
(2) 考虑类:
① const修饰成员变量最好不要在类声明中初始化,而是在构造函数中列表初始化实现对const成员变量的初始化,因为不同的对象可能那个const成员值不一样,但若在类的声明就初始化好了就不可以修改了
② const修饰函数时,不能对非const成员进行修改,除非成员被mutable修饰过
(3) static不考虑类
① 在局部定义一个static变量后,他会被初始化一次,变量不会消失,但也无法被修改
(4) 考虑类
① 由static修饰的成员变量只能在类声明时不能进行初始化,必须在构造函数的成员初始化中初始化,不能在方法里进行初始化,可以被非static成员函数进行访问
② static修饰的函数没有this指针,无法访问非static成员及函数,不能被声明为const, virtual和volatile
(5) const, static修饰的均只能在本文件中被访问
5. 顶层const与底层const理解
(1) 我认为顶层const就是对修饰的变量不能进行修改,如const a;底层const则是修饰的那个东西不能被修改,如const int*a;
6. 数组名和指针区别
(1) 数组名不是真正意义上的指针,因此不能实现自增自减的操作
(2) 当数组作为形参时,会退化成指针
(3) 数组名,指针都可以通过偏移量去访问数据
7. final 和 override关键字
(1) override用于重载,如果基类实现了虚函数,子类想要重写它,可以加上override关键字防止写错
(2) Final用于不想被重写或类不想被继承的情况,虚函数或者类被final修饰,则子函数继承基类时无法对其进行重写或者类无法被继承
8. 拷贝初始化和直接初始化
(1) 拷贝初始化通常是=一个对象或者为其创建一个拷贝对象,直接初始化则是调用构造函数直接成员初始化的方式。
(2) 要注意隐式转换,如果使用了explicit关键字,比如 A a = 1;会编译不通过
9. 初始化和赋值的区别
(1) 针对基本数据类型,初始化与赋值没有什么区别
(2) 针对复杂的类对象,初始化会调用构造函数,赋值是在初始化完成后进行的操作,若未初始化则会发生一个隐含的初始化过程
10. 野指针与悬空指针
(1) 野指针指指针定义了,但未被初始化,因此指向了一个未知的区域
(2) 悬空指针指指针用完被释放后,但没被置为nullptr,使用这个指针将会发生未知错误
(3) 解决方法:野指针定义时初始化或者设为nullptr,悬空指针释放后将指针置为nullptr
11. C++重载,重写,隐藏
(1) 重载:方法名相同,参数,返回类型可能不同
(2) 重写:子类对基类的重写,参数,返回类型均相同,用于虚函数
(3) 隐藏:方法名相同,参数相同,返回类型相同,不过不是去实现基类的虚函数而是重新再写个方法
12. 浅拷贝与深拷贝的区别
(1) 浅拷贝指的是拷贝对象指向的值都是被拷贝对象值的地址,共用一个地址,深拷贝则是自己创建一个新的地址并将值赋给新地址
13. 内联函数与宏定义区别
(1) 内联函数发生在编译阶段,宏定义预处理阶段
2022/11/11
1. C++中有几种类型的new?
(1) Plain new 当分配的内存不够时返回bad_alloc异常。
(2) Nothrow_new 当分配内存不够返回NULL
(3) Placement new 将内存分配到已存在的内存空间
2. static 作用
(1) 隐藏,只能在本文件中使用
(2) 保持变量内容的持久,在局部中使用,一次初始化将会保存下来,不再被修改
(3) 默认初始化为0
(4) 类中的static成员函数不能访问非static成元函数及成员
(5) 在类中使用static成员变量必须在类外进行赋值等操作
(6) 类的所有对象的static成员变量都是一个
(7) static不能被virtual修饰,因为虚函数实现重写需要this指针去指向vptr指针,而static没有this指针
3. C++的集中异常类型
(1) Bad_typeid:当有虚函数的基类初始化为空时,会报错
(2) Bad_cast:当发生基类指针向派生类发生转换时将会返回一个null,当发生指向基类的引用向派生类引用的转换时,会报bad_cast异常
(3) bad_alloc:当采用默认的plian new分配的内存不够时,会报bad_alloc异常
(4) Outofrange:当vector或者list发生越位访问会引发异常
4. 值传递,指针传递,引用传递效率及区别
(1) 值传递:需要在函数指向的栈空间里对形参进行一个值类型的拷贝,耗空间及时间
(2) 指针传递:同样也需要在栈空间里进行一个拷贝,拷贝的是实参的地址,因此改变它的指向不会影响原来的实参,只有改变指针指向的变量才会影响
(3) 引用传递:是对实参地址的引用,改变它将会影响实参
5. 静态变量什么时候初始化
(1) 初始化只有一次,但赋值可以多次。默认初始化为0
6. const关键字有哪些作用
(1) 修饰类的成员函数,不能对类的成员进行改变
(2) 防止变量发生改变
(3) 修饰形参时将不能对形参发生改变
(4) 当类的成员函数返回类型被const修饰时,将不能作为左值
(5) const成员变量不能调用非const成员函数
(6) const必须在定义时就初始化
7. delete[]是如何知道要释放内存的大小的
(1) 当创建了指向了复杂类型数组的指针时,系统会为其多分配4字节的内存专门存放数组的大小
(2) 当执行delete[],我们首先调用析构函数然后delete,因为起始的指针存放的是数组大小,因此没有析构函数,直接delete掉。随后根据数组大小循环执行析构,delete
8. malloc, realloc, calloc的区别
(1) Malloc:分配内存。需要指定大小
(2) Calloc:分配内存,需要指定创建多少个该数据类型
(3) Realloc:分配内存,当内存空间不足时,会自动进行扩充
9. 类成员初始化方式,构造函数执行顺序,为什么使用列表初始化快一些
(1) 初始化方式:列表初始化,构造函数体内初始化
(2) 构造函数执行顺序:虚拟基类->基类->类类型成员->派生类
(3) 列表初始化相当于直接进行初始化,而函数体内初始化相当于赋值操作,多了一步产生临时对象的操作。
10. 哪些情况用到列表初始化
(1) const成员变量
(2) 调用基类的构造函数且含有一组参数
(3) 调用一个成员类的构造函数且含有一组参数
(4) 初始化引用变量
11. C++中新增string,它与char*有什么区别
(1) string继承自basic_string,是对char*的封装
(2) string可以实现动态扩展,在每次扩展的时候另外申请一块两倍空间大小的内存,然后将原字符串拷贝过去,再加上新增的内容。