博主普通双非二本渣渣,秋招时对一些经典的面试题做了一个总结,希望大家多多包涵,如果对大家有所帮助那是最好的了。
因为是个人总结可能有所错误,希望大家包涵,如果有也可以向我提出
关键字static的作用是什么?
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(第一次被函数调用的时候执行,但是第二次调用不执行)。
2). 在模块内(但在函数体外),一个被声明为静态的变量(静态全局变量)可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
4).修饰成员变量,成为类的全局变量,被类的所有对象共享,包括派生类,所有对象维持一个实例,只能在类外初始化,如果要在类内+const
5).修饰成员函数,类只存在一个函数,所有成员共享,只能访问static成员变量,可不用实例化也可访问,不可以同时用const
和static修饰成员函数
引用与指针的区别是什么?
指针就是一个变量,变量存放的是地址,占用内存空间
引用就是变量的别名,是同一个东西,不占内存空间
1、引用必须在定义时被初始化,指针可以不用
2、引用初始化以后不能改变其值,指针可以改变所指向的对象
3、引用不能为空,指针可以为空指针
4、sizeof
引用为引用对象的大小sizeof
()指针为指针的大小
5、引用只能一级,指针可以多级
内存四区?
代码区:存放函数体的二进制码,由操作系统管理
全局/静态存储区:存放全局变量,静态变量(包括static全局和局部变量)
常量存储区:常量 (const
) 存于此处 , 此存储区不可修改
栈区:存放局部变量,函数参数,编译器自动分配+释放
堆区:程序员主动分配释放(如果不释放,操作系统自动回收)堆区比栈区的容量大
意义:不同四个区存放的数据赋予了不同的生命周期,使编程更加灵活
函数
1、函数形参列表的形参是可以有默认值的
2、函数形参列表可以有占位参数,函数调用时必须填补占位参数空缺
3、函数重载:
同一个作用域下
函数名称相同
函数参数类型不同 或者 个数不同 或者 顺序不同
struct和class区别
C++中区别在于访问的权限不同
struct默认是公共权限
class 默认是私有权限
谈谈new,delete,malloc,free
new,delete是C++中的运算操作符,使用时会调用构造函数和析构函数,malloc,free是C中的库函数,都是用来,开辟动态内存与释放动态内存的
C++中只能用运算操作符,在对对象自动执行构造和析构函数时库函数不在编译器管辖范围无法使用
C++中对象创建时要执行构造,消失时要执行析构,所以使用new和delete更好,他们会触发构造和析构函数
一个源码到可执行文件的过程
1、先执行预处理,产生.ill
文件(处理#内容的代码,删除注释)
2、编译,产生汇编文件 .s
文件(词法分析,语法分析,语义分析,中间代码生成,代码优化,生成汇编代码)
3、汇编,产生目标文件 .o,.obj
(将汇编代码转换为机器代码)
4、链接,产生可执行文件 .exe/.out
什么是虚函数,纯虚函数?虚函数的作用?
虚函数在定义时结构中增加了一个虚函数指数表
纯虚函数相当于一个函数接口,只声名不定义,利用子类重写父类的纯虚函数实现多态。
纯虚函数所在的类是抽象类,无法实例化对象。
区别:定义一个函数为虚函数不一定函数不被实现,但如果定义为纯虚函数,函数一定不会被实现
构造函数不能定义为虚函数,但是析构函数可以,因为虚函数执行需要有虚函数表,但是在执行构造函数的时候虚函数表还没有初始化
虚函数的作用:使基类指针指向其他派生类对象时,使基类指针调用派生类对应的虚成员函数
头文件<>和""的区别
遇到<>时,系统从默认的头文件的目录开始寻找
遇到“”时,系统先从所在目录寻找,如果没有找到返回系统默认的头文件目录寻找
函数调用过程
int main(void)
{
...
d = fun(a, b, c);
cout<<d<<endl;
...
return 0;
}
----main函数----
1、参数拷贝,进行压栈,顺序c,b,a
2、保存函数后下一条代码cout...
3、跳转至函数钟
----fun函数----
1、形成新的栈帧结构
2、压栈push形成临时变量执行函数内操作
3、return一个值
4、出栈
5、恢复main函数的栈帧结构
6、返回main函数
const
关键字:
const
修饰全局函数:将作用范围改为本模块,不允许修改其值
const
修饰局部变量:借助volatile关键字使得程序每次直接去内存中读取变量值而不是读寄存器值,实现不允许修改值
const
修饰局部指针:
3是整形常量的指针,指向的内容的值不可以修改
4是指向整型的常量指针,指向的内容不可以改,但是内容的值可以修改
C++中怎么样出现死循环?
1、for语句中S2没有声名或者while语句中的条件处于永久true状态
2、逻辑错误 if(a=1)//原来想写是否等于写成了赋值
3、无穷递归,递归没有增加结束条件
4、看似无限循环其实有界,例如每个类型例如int
类型有范围
编译错误和运行错误的情况
1、编译错误:一般指发生词法错误,语法错误,语义错误,例如关键词用错,类型不匹配,int
赋给了string,少分号
2、运行错误:代码编写和输入数据的问题,例如数组越界,除以0,溢出栈
关于const
和static
以及普通的成员变量初始化的问题:
1、static初始化的成员变量属于类级别,不是对象级别,只能在类内生命类外初始化,且类外初始化的时候不要加static
2、const
初始化的成员必须直接初始化,不可以声名后再赋值。可以在类内直接初始化或在构造函数初始化,不可以在类外初始化或者在构造函数内部进行二次赋值。
this指针
this指针指向被调用的成员函数所属的对象
友元
友元的目的就是让一个函数或者类 访问另一个类中私有成员
全局函数,成员函数和类都可以做友元
sizeof
空类会是什么样的?
由于类在被实例化时一定会分配内存,所以一定会有占用空间大小,对于VS来说,sizeof
一个空类的大小为1
类空可以继承吗
可以的
extern关键字
定义在变量或函数前,标识变量和函数定义在其他文件中,让编译器去其他模块中寻找
auto
自动推断类型
可以用在STL
的迭代器内
vector<double> score;
auto pv=score.begin();
override重写overload重载
重写是指子类重写定义父类虚函数的方法
重载允许函数同名,只是函数的参数的数量,类型不同
多态性
实质:一个接口,多个
多态与非多态的实质区别就是函数地址是早绑定还是晚绑定
虚函数表
对象不包括虚函数表
内联函数,宏定义和普通函数的区别
内联函数要做参数类型检查
编译器如何区分全局变量和局部变量?
通过内存的位置,全局变量存储在全局区程序运行时被加载,局部变量分配在栈中
隐藏指什么?
指派生类的函数屏蔽了与其同名的基类函数
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆) (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
如何让某个类只能通过new来创建(即如果直接创建对象,编译器将报错)
将析构函数设为私有
如果构造设为私有,对象都无法创建
reserve 和 resize 的区别
reserve只修改capacity大小,不修改size大小,resize既修改capacity大小,也修改size大小
resize既分配了空间,也创建了对象,可以通过下标访问。当resize的大小
构造函数内部干了什么?
1.在内存中创建一个新的空对象
2.让this指向这个新的对象
3.执行构造函数里面的代码,给这个新的对象添加属性和方法
4.返回这个新的对象(所以构造函数里面不需要return)
const
常量与define宏定义的区别?
1、编译器处理方式不同
define宏是在预处理阶段展开。
const
常量是编译运行阶段使用。
2、类型和安全检查不同
define宏没有类型,不做任何类型检查,仅仅是展开。
const
常量有具体的类型,在编译阶段会执行类型检查。
3、存储方式不同
define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定义分配内存。)
const
常量会在内存中分配(可以是堆中也可以是栈中)。
4、const 可以节省空间,避免不必要的内存分配。 例如:
#define PI 3.14159 //常量宏
const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ......
double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份
const
和static的区别?
1、static定义的变量在程序初始化的时候会初始化在静态数据区,程序运行期间完全不变,是指这个区域不变,而内容是可以改变的
const
关键字定义的是只读变量,一经赋值,再也不能改变了。const
代表只读变量,而不是常量,常量通常是用enum
或者宏来定义的
构造函数和析构函数的调用顺序?
1、构造函数的调用顺序
基类构造函数、对象成员构造函数、派生类本身的构造函数
2、析构函数的调用顺序
派生类本身的析构函数、对象成员析构函数、基类析构函数(与构造顺序正好相反)
先构造的后析构,相当于一个栈,先进后出
vector和list底层实现
1、vector是动态数组,在堆中分配内存,元素连续存放,有保留内存,如果减少大小后,内存也不会释放;如果新值大于当前大小时才会重新分配内存
支持随机存取,即[]操作符。
对头部和中间进行添加删除元素操作需要移动内存,如果你得元素是结构或类,那么移动的同时还会进行构造和析构操作,所以性能不高。
如果元素是结构或类,最好是将结构或类的指针放入vector中,这样不仅能够节省空间,而且可以避免移动时构造和析构操作
2、list是双向链表,元素也存放在堆中,每个元素都是放在一块内存中,他的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随机存取变得非常没有效率,因此它没有提供[]操作符的重载。但是由于链表的特点,它可以很有效率的支持任意地方的删除和插入操作。
没有空间预留习惯,所以没分配一个元素都会从内存中分配,没删除一个元素都会释放它占用的内存
访问开始和最后两个元素最快,其他元素的访问时间都是O(n)
sizeof和strlen的区别?
1、sizeof是运算符,可以以类型、函数、做参数,在编译的时候就将结果计算出来了是类型所占空间的字节数 。
2、strlen是函数,只能以char(字符串*)做参数,在运行的时候才开始计算结果,这是计算的结果不再是类型所占内存的大小,数组名就退化为指针了。
什么是内存泄漏?怎么产生的?如何检测?解决方法?
1、动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。
4、解决方法:使用智能指针auto
野指针和悬空指针?
野指针:指的是没有被初始化过的指针
悬空指针:指针指向的区域已经被释放
产生原因及解决办法:
野指针:指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。
悬空指针:指针free或delete之后没有及时置空 => 释放操作后立即置空。
排序算法复杂度
冒泡排序:最好的情况是数据本来就有序,复杂度为O(n);最差的情况是O(N^2),稳定算法。
选择排序:最好的情况是数据本来就有序,复杂度为O(n);最差的情况是O(N^2),不稳定算法
直接插入排序:最好的情况是数据本来就有序,复杂度为O(n);最差的情况是O(N^2),稳定算法
希尔排序:最好的情况复杂度为O(n);最差的情况是O(N^2),但平均复杂度要比直接插入小,不稳定算法
快速排序:最好的情况复杂度为NlogN,最差的情况是O(N^2),不稳定
归并排序:所有情况下都是NlogN,稳定算法。
STL底层实现
vector :动态数组
查询和尾部插入删除时间复杂度0(1)
list :链表
任意位置插入删除复杂度O(1),查找元素代价高
deque :双向队列
头尾插入删除时间复杂度O(1)
set :集合
map :映射
四种强制转换
const_cast:里边的内容必须是引用或者指针
修改指针
编译阶段
作用是去掉一个类的这些属性:const,volatile和_unaligned
常量指针被转化成非常量的指针,并且仍然指向原来的对象; 常量引用被转换成非常量的引用,并且仍然指向原来的对象;
static_cast:
与C语言强转效果相同但是没有类型检查,有安全隐患
数据类型之间的转换
类层次结构基类和派生类的转换
非const转const
reinterpret_cast:里面的内容必须是一个指针、引用、算术类型、函数针或者成员指针
主要用于将一种数据类型从一种类型转换为另一种类型。它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针。
有问题,尽量少用
dynamic_cast:
运行期间看转换可能
用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用
向下转化时,如果是非法的对干指针回NULL,对于引用抛异常。要深入了解内部转换的原理
向上转换:指的是子类向基类的转换
向下转换:指的是基类向子类的转换
它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换
智能指针
unique_ptr
unique_ptr<string> p3 (new string ("auto")); //#4
unique_ptr<string> p4; //#5
p4 = p3;//此时会报错!!
保证同一时间只有一个智能指针指向对象
shared_ptr
多个智能指针可以指向相同对象
weak_ptr
当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。
weak_ptr是用来解决shared_ptr相互引用时的死锁问题
C++11新特性
auto关键字:编译器可以根据初始值自动推导出类型。但是不能用于函数传参以及数组类型的推导
nullptr关键字:nullptr是一种特殊类型的字面值,它可以被转换成任意其它的指针类型;而NULL一般被宏定义为0,在遇到重载时可能会出现问题。
智能指针:C++11新增了std::shared_ptr、std::weak_ptr等类型的智能指针,用于解决内存管理的问题。
初始化列表:使用初始化列表来对类进行初始化
右值引用:基于右值引用可以实现移动语义和完美转发,消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率
atomic原子操作用于多线程资源互斥操作
新增STL容器array以及tuple
三次握手
SYN--请求建立连接 报文
ACK--确认号是否有效
seq是序号,ack是确认号
客户端发送syn报文,序号为x,服务端发送syn+ack报文,序号为y,确认序号为x+1,客户端发送ack报文,序号为z,确认序号为y+1
多重继承的构造顺序
调用父类构造函数的顺序是继承时的顺序(父类->爷爷类->...子类),最后调用子类构造函数。
栈和堆的区别,以及为什么栈要快
堆是由低地址向高地址扩展;栈是由高地址向低地址扩展
堆中的内存需要手动申请和手动释放;栈中内存是由OS自动申请和自动释放,存放着参数、局部变量等内存
堆中频繁调用malloc和free,会产生内存碎片,降低程序效率;而栈由于其先进后出的特性,不会产生内存碎片
堆的分配效率较低,而栈的分配效率较高,因为 栈是操作系统提供的数据结构,计算机底层对栈提供了一系列支持:分配专门的寄存器存储栈的地址,压栈和入栈有专门的指令执行;而堆是由C/C++函数库提供的,机制复杂,需要一些列分配内存、合并内存和释放内存的算法,因此效率较低。
deque和queue区别
deque 双端队列
可以访问两端并且可以在队首和队尾删除和插入元素
queue 队列
只能从队首删除元素,但是两端都能访问
为什么要将基类的析构函数设为虚函数?
用基类指针指向new出来的子类时,根据 动态绑定可以确保子类新添加的部分也可以被顺利释放掉,以防出现内存泄漏
SET容器自定义内容排序
重载()