整理
-
对class的理解
答:首先,class 也是一种数据类型,只不过是可以由开发者自由定义的一种数据类型;可以用来封装成员变量和成员函数;支持抽象,继承,多态性;在定义数据类型的时候,private,public,也从一定程度上保证了数据的安全性
抽象性:含有纯虚函数的类被称为抽象类,用途是为派生类提供基类
多态性:对于不同对象接受相同信息时产生不同的动作。分为静态多态和动态多态,体现在两个方面,一是在编译的时候,函数重载是一个方面;二是在运行的时候,虚函数是一个方面
继承性:子类具有父类的各种属性和方法,而不必再次编写相同的代码
-
什么是类
答:一种将抽象转换成用于定义的类型的工具,将数据表示方法和操纵这些数据的方法组合一起整合成一个包
-
如何将私有成员变量取出
答:方法1:通过public中的函数,传出参数将私有成员变量取出。方法2:友元函数
-
*this 指针
答:经典回答,进入一个房间,能看见房间里的东西,但是整个房间看不到了,然后this指针就是时刻盯着这整个房间。this指针是类的一个自动生成,自动隐藏的私有成员。存在于类的非静态成员函数中,指向内调用函数所在的对象。全局仅有一个this指针,当一个对象被创建的时候,this指针就存放指向对象数据的首地址
-
类和对象的区别和联系
答:区别:(1).定义不同 ,类是现实世界或思维世界的实体在计算机的反映,他将数据以及对数据的操作封装在了一起。对象是有具体类型的变量
(2) 范畴不同,类是一个抽象的概念。对象是类的一个具体,是真是存在的东西
(3)状态不同,类是一个静态的概念,当没有为类创建任何数据的时候,类本身不存在于内存空间中
联系:类是对象的抽象,而对象是类的具体事例 。鞋是同一个类,白鞋,黑鞋,,这些是对象
-
值拷贝&&内容拷贝 浅拷贝&&深拷贝
答:浅拷贝对应于值拷贝,深拷贝对应于内容拷贝。浅拷贝是两个对象操作同一块内存,而深拷贝是先创建相同大小的内存,然后将内容拷贝过来;
-
拷贝构造函数和赋值函数的区别:
答:从概念上来说,第一个是构造函数,第二个是赋值函数;从使用场景来说,第一个一般用于初始化各种参数,第二个是要求 = 两边均存在
-
类中析构函数和构造函数调用顺序:
答:析构函数,是先调用派生类的析构函数,然后是对象成员的析构函数,然后再调用基类的析构函数;而构造函数正好相反
-
虚函数,纯虚函数
答:虚函数,在基类中的某一个成员函数前面加上virtual关键词,提供了一种接口界面,允许在派生类中对基类的虚函数进行重新定义,这就是多态性很好地一个体现。当在基类中把成员函数定义为虚函数的时候,如果要在派生类中调用这个虚函数,参数类型,顺序,个数都必须相同,但是对数据的操作是可以变化的。
虚函数的实现,在有虚函数的类中,会有一个指向虚函数表的指针,表中存放了虚函数的地址,当子类继承了父类的时候也会继承虚函数表;当子类重写虚函数的时候,会将继承到的虚函数表的地址转换为重新写的函数地址。使用了虚函数,会增加访问内存的开销,降低效率
纯虚函数:含有纯虚函数的类不能被声明对象,在基类中仅仅给出声明,不对虚函数实现定义,实现定义是在派生类中实现的。 含有纯虚函数的类被称为抽象类,继承于抽象类的派生类如果不能实现基类中所有的纯虚函数,那个这个派生类也是抽象类。
-
=default 和 =delete
答:这种行为只是对构造,赋值,析构这三类特殊的函数设计的,=default是为了让编译器合成默认的构造函数。=delete是为了不想让编译器合成某些函数
-
explicit关键词
答:在类的构造函数中,如果只是对一个参数(>=2时不行)的构造函数,如果要防止类构造函数的隐式自动转换,可以在该构造函数前面加上explicit关键词,就可以避免这个。
-
new/delete , new[]/del[]
答:开空间的时候实质上还是通过malloc实现的,new用于单个对象或者实例的创建,在申请单个类型变量的时候可以进行初始化,delete和new配套使用。new []是多个对象或者实例以数组的形式来进行创建的,每个对象都会调用构造函数;delete[]调用的时候,一次调用对象数组中每个对象的析构函数,然后再将内存释放掉,
-
new和malloc区别
答:首先,new/delete是c++ 关键词,而malloc/free 是C语言的库函数 1)malloc只是动态分配内存,而new除了动态分配内存还会调用构造函数来对创建出的对象或者实例进行初始化。 2)new是从自由存储区(可以为堆,也可以是静态存储区)为对象动态分配内存空间,而malloc是从堆上动态分配内存 3)返回类型不同,new返回的是对象类型的指针,不需要进行强制的类型转换。而malloc是返回的泛型指针,需要进行强制的类型转换 4)内存分配失败时的返回值,new抛出异常,而malloc会返回NULL 5)new不需要指定大小,会根据类型信息自动计算,而malloc需要指定大小 6)当分配内存的时候,new 能通过指定处理函数或者重新指定分配器,而malloc无法通过用户代码处理 7)new允许函数重载,malloc不允许函数重载
-
重载new 和 delete操作符的好处
答:通过重载new 和 delete 操作符,我们可以做一些内存管理工作。比如果加一个计数器,在new的时候就计数加1, delte的时候就-1 ,这样最终执行完程序的时候,统计这个计数器是否为0,如果不为0就证明有的对象没有被delete,这就有可能到这内存泄漏
-
vector的底层实现
答:当我们担心空间不够而开很大的数组的时候,vector可以很好地优化这一点。vector在定义的时候会先申请一个空间,当占用的这个空间到达一定比例的时候,vector会自动向内存中申请一段空间,然后把原来的数据迁移到这个新申请的空间中,然后释放掉原有的空间,但是重新分配空间是一个很耗时的操作,而vector对此进行了许多优化。注意:当扩容的时候,vector会进行内存的一个重新分配,和vexctor有关的迭代器就会失效
-
allocator 的作用
答:分配内存;构造对应的对象,析构函数;释放内存
-
智能指针类
答:
动态内存管理经常出现两种情况,一种是申请的内存忘记释放,造成内存的泄漏;另一种是还有指针引用内存的时候就把这个内存释放掉了,会产生引用非法内存的指针.为了更加安全的使用动态内存,引入了智能指针这种概念
shared_ptr , 共享式指针管理类。内部有一个引用计数,每当有一个新的shared_ptr指针指向同一个被管理的内存资源的时候,计数器会加一,当计数器为0的时候,这个内存资源会被释放。
unique_ptr,独占式指针管理类,某个时刻只能有一个unique_ptr指向一个给定对象。 当unique_ptr指针所指向的对象被销毁/重置的时候,这个内存资源会被释放。当一个unique_ptr指针赋给另一个unique_ptr指针的时候,内存资源的管理从源对象转移到目标对象
weak_ptr ,是不控制对象生命周期的智能指针,指向一个shared_ptr管理的对象,这个指针的构造和析构并不会引起计数的增加或者减少。可以用来解决shared_ptr造成的死锁问题
-
initializer_list
答:initializer_list,可以用来初始化列表语法将STL容器初始化为一系列值。(处理长度不同的列表),注意,initializer_list的迭代器类型为const,所以不能修改initializer_list中的值
-
const 和 constexpr
答:cosnt代表对变量的一个修饰,告诉编译器这个变量被初始化,并且不能直接修改
,既可以在编译的时候指定,也可以在运行的时候指定;constexpr可以用来修改变量,函数,构造函数。(看成编译时就能得出常量值得表达式),相对于宏来说,没有额外的开销,并且更加安全可靠;在编译器对constexpr的代码进行非常大的优化,比如将constexpr 表达式都直接替换成最终结果
-
static_assert
答:static_assert(bool_constexpr ,message);当bool_constexpr返回false的时候,编译器将报错,报错的内容是message
-
引用和指针
答:引用就是某个变量的别名。引用和指针在做参数及做返回值类型上几乎效率相同,远远高于传值。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量的一个别名,引用本身不占存储单元。
不同点:1. 引用在定义的时候必须初始化,指针没有要求。2. 引用只能初始化用一个实体,而指针可以在任何时候指向任何一个同类型实体 3. 没有NULL引用,但是有NULL指针。4. 在sizeof中,引用的结果为引用类型的大小,而指针始终是地址空间自己所占的字节个数 5. 引用自加即引用的实体+1,而指针自加是指针往后偏移一个类型的大小 6. 引用使用起来比指针更加简洁,安全 7. 指针可以有多级指针,而引用只有一级
-
将 “引用”作为函数参数有哪些特点?
答:
-
首先 和 传递 指针的效果是一样的
-
使用引用传递函数的参数,在内存中并没有产生实参的副本,是直接对实参进行操作的;而如果使用一般变量传递函数的参数的话,会产生变量的副本,如果传递的参数的数据较大的时候,用引用比用一般变量传递参数的效率和所占的空间都好
-
代码更加清晰,易读
-
在什么时候需要使用“常引用”?
答:如果既要利用引用提高程序的效率,又要保护传递给参数的数据不在函数中被改变
-
将引用作为函数返回值类型的好处和需要遵守的规则?
-
好处:在内存中不会产生内存的副本
需要遵守的规则:
-
不能返回局部变量的引用,因为局部变量在函数返回的时候会被销毁,所以被返回的引用就会成了一个无用的引用
-
不能返回函数内部new分配的内存的引用。虽然不会有上一个情况的发生,但如果被函数返回的引用只是一个临时变量,那么这段空间就不会被释放
-
struct 与 union 的区别:
答:1. struct 和 union 都是有多个不同的数据类型成员构成的,但是在任意一个时刻,union中只存一个被选中的一个成员,而struct的所有成员都存在,struct变量的总长度等于这个结构体内部所有成员的总和。而union中的成员不能同时存在,union变量的产固定 等于union中成员长度最长的那个
-
对于union中不同成员赋值,将会对其他成员进行重写,原来的成员就会被覆盖,而struct不会
-
重载 和 重写的区别?
答:从定义上说:重载是允许存在多个同名函数,而函数的参数有所区别,或者返回值;重写是子类重新定义父类虚函数的一个方法
从实现原理上说:
重载的调用,在编译期间就已经确定了,这是一个静态的,和多态并没有关系
重写的调用,当子类重新定义了父类的虚函数的时候,父类指针根据赋给她不同的子类指针,动态的调用属于子类的函数,在编译期间无法确定
-
什么时候只能用 intialization list 而不能用 assignment ?
答:当类中函数const,reference(引用)成员变量 ;类的构造函数需要调用其基类的构造函数
-
描述内存分配方式以及他们的区别?
答:从静态存储区分配,在编译的时候就已经分配好,如全局变量,static变量;
从栈分配,在执行函数的时候,函数内部局部变量的申请都可以在栈上申请空间,当函数执行完后这部分内存自动释放。
从堆上分配,动态分配。当运行malloc 或者new 的时候,会在这上面申请
-
const 和 define 相比的优点?
答:const 的作用,定义常量,修饰函数参数,修饰函数返回值。通过const修饰的东西会受到强制保护,可以预防意外的变动,提高程序的健壮性
const常量有数据类型,而define没有。在编译的时候,编译器可以对前者的进行一个检查
-
数组与指针的区别?
答:1)赋值的时候,同类型的指针变量可以相互赋值,而数组不行,只能一个一个元素的赋值或者拷贝 2)sizeof可以计算出数组的大小,而sizeof对指针来说,只是当前指针变量的字节数 3)数组在内存中是连续存放的,而指针的存储空间不能确定 4)指针存储的是地址,数组存储的是数据 5)指针是间接访问数据,通过指针保存的地址来访问数据,数组是直接访问数据
-
基类的析构函数不是虚函数,会带来什么问题?
答:派生类的析构函数用不上,造成资源的泄漏
-
全局变量和局部变量的区别?
答:生命周期不同:
全局变量随着主程序的运行而创建,局部变量是在函数内部申请的,当函数返回的时候,这个变量就会被销毁
使用方式:
全局变量在函数的各个部分都能使用,而局部变量只能在局部使用
通过内存分配的方式来判断,全局变量在主程序运行的时候,分配在全局数据段。局部变量是在堆栈中
-
static关键词
答:函数或代码块中的变量在函数或者代码块第一次初始化分配内存的时候,就算函数或者代码块执行完毕,这个变量也不会回收,直到程序结束才会被回收;加了static 关键词的全局变量只能在本文件使用。static定义的静态局部变量分配在数据段,普通的局部变量分配在栈上;对于一个类中的成员变量和成员函数来说,加了staitc关键词,这个函数或者变量就没有this指针了
不可重入性;记忆性;
-
c 和 c++ 区别
答:设计思想上,c++是面向对象的语言,c是面向过程的结构化编程
语法上,c++ 具有重载,继承,多态三种特性;c++相比c增加了许多类型安全的功能,比如强制的类型转换;c++ 支持范式编程,如模板类,函数模板等
-
static 关键字的作用
答:
1) 全局静态变量
-
存储在静态存储区中,在整个程序运行期间一直存在
-
自动初始化为0
-
全局静态变量在声明他的文件之外是不可见的
2)局部静态变量
-
存储在静态存储区中,作用域为局部作用域每当定义它的函数或者语句块结束的时候,作用域结束。但是这个变量并没有被销毁,仍然存在内存中,直到函数被再次调用
3)静态函数
-
在函数返回类型前面加static,函数就定义为静态函数,这个函数只能在本cpp内使用,不会同其他cpp中的同名函数起冲突
4)类的静态成员
-
在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则性,也保证了安全性、对多个对象来说,静态成员数据只存储在一处,供所有的对象共用。
5)类的静态函数
-
静态成员函数和静态成员都属于类的静态成员,都不是对象成员。在静态成员函数的实现中不能直接引用类中说明的非静态成员,但是可以引用类中说明的静态成员,如果静态成员函数要引用非静态成员,可以通过对象来引用
-
抽象和接口
答:抽象类,一个类中只要含有纯虚函数,那么它就是抽象类;接口,一个类中,没有定义任何成员变量,所有的成员函数都是公有的,不能有private修饰符,并且所有的成员函数都是共有并且都是纯虚函数
-
进程和程序的主要区别
答:进程是程序的一次执行。具有动态性:是程序的一次执行;并发性:进程是可以并发执行 ;独立性:系统进行资源分配和调度的一个独立单位;异步性:进程间的相互制约,使得进程具有间隙;结构性:进程是具有结构的
区别:程序时永存的,进程是暂时的;程序是静态的观念,进程是动态的观念;进程具有并发性,而程序没有;进程和程序不是一一对应的,一个程序可以执行多个进程,一个进程可以执行一个或者多个程序
-
类的静态成员和非静态成员有何区别?
答:非静态成员也叫成员变量 。1) 名称上的区别。成员变量也叫实例变量;静态变量也叫类变量 2)内存存储的区别,成员变量存储到堆内存中,静态变量存储在静态区中
3)声明周期不同,成员变量随着对象的出现而出现,随着对象的消失而消失;非成员变量随着类的创建而出现,随着类的消失而消失
-
extern关键词
答:1)声明extern 关键字的全局变量和函数使得他们能够跨文件被访问 2)extern 是c/c++ 表明函数和全局变量作用范围的关键字,声明的函数和关键字可以在本模块使用或者其他模块使用 。注意,extren int c;这个语句,并没有对int申请空间,仅仅是声明一个变量 3) 在c++中引用C语言的函数和变量,(C语言没有函数重载)通过extern "C"{ ----- }来实现
-
extern 和 static 的区别
答:1)作用范围不同,extern 是外部存储类型,属于全局变量是除了本编译单元可以使用,别的编译单元也可以使用这个变量;但是static是静态存储类型,只允许本编译单元使用这个变量
-
c++ 中的四种转换
答:
1)static_cast 静态类型转换,不可用于多态,静态类型的转换 ,可以用于任意类型的指针之间的转换,如非const转const,void * 转指针
2)dynamic_cast,动态类型的转换,只能用于含有虚函数的类,用于类层次间的向上(子类向基类的转换)和向下转换,只能转指针或者引用
3)reinterpret_cast,大部分都能转,对转换的结果不做任何保证
4)const_cast, 将const 转成非const
-
野指针?
答:指向一个已经删除的对象或者未申请访问受限内存区域的指针
-
智能指针的内存泄漏问题如何解决?
答:为了解决循环引用导致的内存泄漏,引入了weak_ptr弱指针,weak_ptr的构造函数不会修改引用计数的值。类似于一个普通指针,但是不指向计数的共享内存。可以检测到所管理的对象是否已经被释放,从而避免非法访问
-
为什么析构函数必须是虚函数?
答:析构函数的调用顺序是先派生类的调用析构函数,然后再对象成员的析构函数,最后是基类调用析构函数。如果没有将析构函数设置成虚函数,当我们new一个派生类并且基类指针指向这个派生类,在销毁基类指针的时候只会调用基类的析构函数,而不会调用派生类的析构函数,因为基类无法操作派生类中非继承的成员,这样就会造成派生类只new无法delete 的情况
-
为什么c++ 默许的析构函数不是虚函数?
答:c++ 默许的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚变指针,会占用额外的内存、如果一个类没有继承,并且它的析构函数还是虚函数的话,就会浪费内存
https://blog.csdn.net/Code_GodFather/article/details/6648033#commentBox
-
说一下函数指针?
答:定义,函数指针就是指向函数的指针,有了函数指针之后,可以用这个指针变量调用函数,比如常用的回调函数
-
c++中析构函数的作用?
答:当对象声明周期结束的时候,如果对象所在的函数已经调用完毕,系统会自动调用析构函数来收回内存。
-
写个函数在main函数执行之前运行
答:
__attribute((constructor))void before()
{
printf("before main\n");
}
-
c++ 定义常量的方法。常量存放在内存的哪个位置?
答:const + 对象类型 ,常量类型必须初始化。
-
局部变量,存放在栈区
-
全局变量,存放在静态存储区
const的作用,保护常量, 修饰函数参数,修饰函数返回值
const char * arr = "123";
char *brr = "123";
const char crr[] = "123";
char crr[] = "123";
第一个 和 第二个 是相同的,指针指向的是同一个位置、
第三个是存储在栈上的,但是编译器可能会将其放到常量区(常量区不允许修改)
第四个保存在栈区,可以通过drr来修改
-
c++ 函数栈空间的最大值
答:默认是1M,不过可以调整
-
虚函数表实现机制
答:https://blog.csdn.net/caoshangpa/article/details/80112673 这人属实nb
1)每一个含有虚函数的类,都会生成虚表 。 这个表,记录的对象的动态类型,决定了执行此对象的虚成员函数的时候,真正执行哪一个成员函数 2)对于有多个基类的类对象,会有多个虚表,每一个基类对应一个虚表。 3)在每一个类对象所占用的内存中,虚指针(void *类型)位于最前面并且指向对应的虚表,注意这个歌虚指针并不属于类对象的一部分
-
你了解的RTTI
答:运行时类型检查,在C++层面主要体现在dynamic_cast和typeid,VS中虚函数表的-1位置存放了指向type_info的指针。对于存在虚函数的类型,typeid和dynamic_cast都会去查询type_info
-
C语言是如何进行函数调用的?
答:每一个函数调用都会分配函数栈,在栈内进行函数执行的过程。调用前,先把返回地址压栈,然后把当前的函数的esp指针压栈 。 (压栈顺序:从左到右)
-
c++ 是如何处理返回值的?
答:生成一个临时变量,把他的引用作为函数参数传入函数内
-
为何map 和 set 的插入删除效率比用其他序列容器高?
答:这两种数据结构采用的是一种非常高效的平衡检索二叉树,红黑树(AVL数,RB树)。和vector,list相比,不需要进行内存的拷贝 和 内存的移动。map 和 set所有的元素都是通过节点的方式来存储的;当插入的时候,把节点的指针指向新的节点,然后调整树的结构即可;当删除的时候,把指向删除节点的指针指向其他的节点,调整树的结构即可;
-
为何map 和 set每次insert之后,以前保存的iterator不会失效?
答:iterator相当于指向节点的指针,内存不会发生改变,所以以前保存的iterator不会失效
-
vector在进行删减操作的时候,指针为何有可能会失效?
答:1)vector为了保证数据的连续存放,iterator指向的内存在插入或者删除之后可能会已经被其他内存覆盖或者释放。2)当vector内存放的数据到达一定比例时,会重新开辟一个新的内存来扩容,并且把原来的vector中的内容转移过去,这个会导致iterator指向的内存失效
-
STL的基本组成 以及 STL中的allocator(空间配置器)
答:首先介绍六大组件
容器(containers)(各种数据结构),
算法(algorithms),
迭代器(iterators),
仿函数(functors)(行为类似函数,可作为算法的某种策略),
配接器(adapters)(用来修饰容器,仿函数,迭代器结构的东西,ex:STL中的queue 和 stack,其实底层实现都是通过deque),
配置器(allocators),负责空间配置与管理
六大组件的关系:容器通过配置器获得数据存储结构,算法通过迭代器存取容器内容,仿函数可以协助算法完成不同的策略变化,配置器修饰或套接functor(仿函数)。
STL的分配器用于封装STL容器在内存管理上的底层细节,c++中内存配置和释放如下:
new运算分为两个阶段:1)调用::operator new 配置内存 2)调用对象构造函数构造对象内容
delete运算分为两个阶段:1)调用对象析构函数 2)调用::operator delete 释放内存
为了精确分工,STL allocator将两个阶段区分开:内存配置有alloc::allocate负责,内存释放由::construct()负责,对象析构由::destroy 负责。
在SGI版本的STL中 ,为了提高内存管理效率,减少申请小内存造成的内存碎片,SGI STL采用了两级配置器,当分配的空间超过128B的时候,会使用第一级空间配置器;当 <= 128B的时候,将使用第二级配置器;第一级配置器直接使用malloc(),realloc(),free()函数进行内存空间的分配和释放,第二级配置器采用了内存池,通过空闲链表来管理内存
-
为何map 和 set 不能像vector一样有个reserve函数来预分配数据?不太理解
答:map 和 set 内部存储的并不是元素本身,而是包含元素的节点。也就是说map内部使用的Alloc并不是map声明的时候从参数中传入的Alloc。例如: map, Alloc > intmap; 这时候在intmap中使用的allocator并不是Alloc, 而是通过了转换的Alloc,具体转换的方法时在内部通过 Alloc::rebind重新定义了新的节点分配器,详细的实现参看彻底学习STL中的Allocator。 其实你就记住一点,在map和set里面的分配器已经发生了变化,reserve方法你就不要奢望了。
-
当数据增多的时候,map 和 set 的插入和搜索速度变化?
答:log级别
-
为何直接使用定义的库函数还是不如封装的效果好?
答:STL中速度快,不仅仅是调用了函数,还包括内存碎片的处理。STL采用自己的allocator分配内存,通过内存池的形式来管理内存,提升系统的整体性能。
-
map 和 set的区别,分别如何实现的?
答:都是c++ 的容器,底层实现都是红黑树。
区别:1)map中的元素都是key-value(关键字-值)对;关键字起到索引作用,值表示与索引相关的数据;set是关键字的集合,每个元素只包含一个关键值
2)set迭代器是const类型,不能通过修改迭代器,来改变set的值,因为set的值就是键,set不允许出现重复,所有的元素都会自动排序,不支持下标操作
3)map的键和值时分来的,所有的元素都是 键 + 值 存在,不允许出现键重复,所有的元素都是通过键自动排序的,map的键是不能修改的,但是键对应的值时可以修改的,支持下标操作;
4)为什么不允许修改key值,因为如果允许修改key值的话,首先要将这个键删除,然后调节平衡,再插入修改后的值,调节平衡,这样会破坏map和set 的结构,导致iterator失效。
-
STL迭代器删除元素
答:1)对于序列vector,deque来说,如果使用erase(iterator) , 后面的每个元素的迭代器都会失效,erase会返回下一个有效的迭代器 2)对于关联容器map,set来说,使用了erase之后,当前的元素的迭代器失效,但是并不会影响到下一个元素的迭代器 3)对于下一个迭代器来说,使用了不连续分配的内存,并且他的erase会返回下一个有效的iterator
-
STL中的map 和 unordered map
答:map的底层实现是红黑树;unordered map的底层实现是哈希表
-
STL中的map 和 multimap
答:map 和 multimap 中所有的元素都会根据元素的键值自动进行排序。但是map不允许键值排序,multimap 是允许键值排序的;map的适用场景,有序键值对不重复映射,multimap 的有序键值允许重复映射
-
vector 和 list 的区别
答:
-
从概念上来说
vector 是 一个连续储存的容器,动态数组,在堆上分配空间
底层实现:动态数组
当vector插入新的元素的时候,如果这个时候vector已经存储上的数据 和 总的容量相比,达到一定比例后,会进行一个扩容,进行内存拷贝,扩大内存,然后将之间的内存释放掉。如果还没有到达这个比例,就直接添加的需要添加的位置
性能:
访问 O(1)
插入:
如果内存到达一定比例之后,需要进行内存的申请和释放,然后进行内存的拷贝;
当内存足够时:
在后面插入,快 ;在中间插入,进行内存的拷贝;在后面删除,快; 在中间删除,进行内存的拷贝;
适用的场景:
多次访问,并且对非尾节点的删除,访问次数较少
-
从概念上说
list是动态链表,在堆上分配空间
底层实现:双向链表
性能:
随机访问的效率并不高,只能快速访问头结点和为节点
插入:很快
删除:也很快
使用场景:多次删除或添加节点
-
区别
1)vector的底层实现是数组,list是双向链表;2)vector的内存是连续的,list不一定是
3)vector 和 list 的适用场景不同 4)vector当内存不够的时候是动态扩容,而list每次插入节点都是进行内存的申请 5)vector在中间进行节点的插入和删除的时候回有内存的拷贝,list不存在这种情况 6)vector支持下标访问,list不支持
-
STL中 的迭代器的作用 和 指针的区别
答:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示
和指针的区别:迭代器本质是封装了指针,是对指针的一种提升。迭代器是一个可以遍历STL容器内全部或者部分元素的对象。 迭代器返回的是对象引用
迭代器之所以产生,就是可以通过不暴露集合内部结构而达到循环遍历集合的效果
-
STL中的resize 和 reserve 的区别
答:resize:改变当前容器内含有的元素数量,如果原来的size 小于 len ,那么容器会新增元素填充,默认值为0,再新添元素的话,就在len + 1 处添加
reserve:改变当钱容器的最大容量,并不会生成元素,只是确定这个容器允许放入多少对象
-
c++ 中类成员的访问权限
答:c++ 通过成员限定符public , protected , private 三个关键词来控制成员变量 和成员函数的访问权限,在类中,这三种都是可以互相访问的。但是在类的外部,只能访问public成员,不能直接访问 private 和 protected 成员。procted是继承的类可以访问protected的成员,基类的procted成员不能再类外直接访问,但是不能访问private的成员。
-
c++中struct和class的区别
答:struct的默认继承权限和默认访问权限是public,而class是可以控制默认权限的,class还可以定义模板类形参
-
左值和右值的概念
答:左值,能对表达式取地址,或具名对象/变量。一般指表达式结束后仍然存在的持久对象;
右值,不能对表达式取地址,或匿名对象。一般指表达式结束后就不再存在的临时对象
-
什么是右值引用,和左值引用有什么区别?
答:int i = 0; i是左值,0是右值。区别左值和右值的便捷方法:看能不能对表达式取地址,如果能,就是左值,否则就是右值。
左值引用
int a = 10;
int& refA = a;
//这里a是左值,ref是a的别名,修改refA就是修改a,这就是左值引用
右值引用
int&& a = 1;
//实际上是给那个临时变量取了个别名,a的类型是右值引用类型
右值引用实现了转移语义和精确传递。主要目的有两个:
-
消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率
-
能够更简洁的定义泛型指针
右值引用和左值引用的区别:
-
左值可以寻址,而右值不可以
-
左值可以被赋值,右值不可以被赋值,可以用来给左值赋值
-
对于基础的类型,左值可以变,右值不可以变
-
malloc的原理
有一个将可用的内存块连接成一个长长的列表的空闲列表。调用malloc函数的时候,会遍历链表找到一个大于用户所需的内存块。然后将这块内存一分为二(一块给用户,一块剩下)。将剩余的内存块返回到连接表上。当调用free函数的时候,会将用户释放的内存块连接到空闲链上。最后的时候,空闲链会切成很多小的内存片段,这个时候malloc函数就会请求延时,在空闲链表上对内存片段进行整理
-
如何判断内存泄漏
在申请和释放内存的时候添加统计功能
-
什么是内存泄漏?
由于疏忽或者错误造成了程序未能释放掉不再使用的内存
-
内存泄漏的分类:
-
堆内存泄漏,通过malloc,new等从内存中分配的内存,并没有通过 free或者delete释放掉
-
没有将基类的析构定义为虚函数,当基类指针指向子类对象时,如果基类的析构函数不是virtual,子类的析构函数不会被调用
-
红黑树和AVL树的定义,特点,以及两者的区别
AVL树(平衡二叉树),特殊的二叉排序树,树中任何一个节点作为根,那么这个根所代表的子树中,左右子树高度之差不超过1
红黑树(二叉查找树 R-B树,弱平衡二叉树),在每一个节点增加一个存储为标识节点的颜色,确保没有一条路径会比树上的最短路径多出一倍。
相对于AVL树,旋转次数少,对于搜索,插入,删除操作较多的情况下,一般使用红黑树。
红黑树性质:
-
每个节点非红即黑
-
根节点是黑色的
-
每个叶节点都是黑色的
-
如果一个节点是红色,则它的子节点必须为黑色
-
对于任意节点,其到叶子节点的每条路径都有相同数目的黑节点
区别:
AVL树是高度平衡的,频繁的插入和删除,会导致效率下降。红黑树是弱平衡的,对于频繁的搜索,插入,删除,效率比AVL树高
-
哈夫曼编码
主要目的是通过使用频率最大化来节省字符的存储空间,用于数据压缩,加密解密。以自底向上的方式构建最优前缀码的二叉树,每次选取最小的两个节点,作为左右子树构建一个新的树,从自往上构建。
-
map底层为什么要使用红黑树?
红黑树是一种弱平衡二叉树,插入最多有两次旋转,删除最多有三次旋转,在查找,删除,插入的时候,复杂度基本为O(longn),并且性能稳定。AVL树是高度平衡的,如果是频繁的插入,删除效率会降低。
-
B树,B+树
B树是为了实现高效的磁盘存储而设计的多叉平衡搜索树。
通过最小度来定义B树,B树中每个节点最多包含2t -1个关键字
创建目的:如果是按照二叉查找树的形式进行存储每当数据量非常大的时候,访问的节点变多,I/O次数变多,随着树变高,频繁的I/O操作会降低查询的效率,B树的h也是log级别,但是和AVL树相比,树变得矮了,同时也极大地降低了磁盘的读取次数
B树和B+树的区别:
-
B+树只有叶子结点有指向记录的节点,而B树所有节点都带有,在内部出现过的索引不会在叶子节点中出现。
-
B+所有叶子结点通过指针连在一起,而B树不会
B+树的优点:
-
由于非叶子节点不带有指向记录的指针,所有一个节点可以承受更多的索引项,既可以降低树的高度,也可以定位更多的叶子结点
-
叶子节点之间通过指针相互移动,范围扫描的时候非常简单,而B树可能需要再叶子节点和内部节点之间往返运动
B树优点:
-
对于在内部节点的数据,可以直接得到,不必根据叶子节点来定位
-
map和unordered_map的底层实现
map的底层是基于红黑树实现的,因此map内部元素排列是有序的,而unordered_map 的底层是通过hash表实现的,因此其元素的排列顺序是杂乱无序的
map的优点是有序性,搜索,插入,删除的复杂度都为O(logn),比较稳定,平均复杂度较慢,和n有关。
unordered_map优点是内部实现了hash表,所以查找速度非常快。但是缺点是hash表的建立比较耗费时间,查找,删除,添加的速度快,时间复杂度为常数级别。对于查找问题,unordered_map会更加高效一点。
-
epoll是如何实现的
-
执行epoll_create的时候,创建红黑树
-
执行epoll_ctl时,创建就绪的list链表,如果增加fd到红黑树上,然后向内核注册有事件来到的时候的回调函数,当设备上的中断事件来临的时候,这个回调函数会向list链表中插入就绪的fd并且唤醒epoll_wait进程
-
执行epoll_wait时立刻返回准备就绪链表里面的数据即可
-
stack overflow
栈溢出:指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致栈中与其相邻的变量的值被改变
原因:
-
局部数组过大,当函数内部的数组过大时,有可能导致堆栈溢出。解决方法:增大栈空间,改用动态分配
-
递归层次太多,在运行时会执行压栈操作,当压栈次数太多时,会导致堆栈溢出
-
指针或者属于越界
-
栈和堆的区别,为什么栈要比堆块?
堆是由低地址往高地址扩展;栈是由高地址往低地址扩展
-
栈内存存储的是局部变量,而堆内存存储的是实体
-
因为局部变量的声明周期很短,栈内存的更新速度快于堆内存
-
栈内存存放的变量生命周期一结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定的回收
-
分配方式不同,堆都是动态分配,栈可以静态分配和动态分配
-
存放内容不同,栈存放的内容,函数返回地址,相关参数,局部变量,寄存器。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现。
栈是操作系统提供的数据结构,计算机底层对栈提供了一系列支持;分配专门的寄存器存储栈的地址,压栈和入栈有专门的指令执行;而堆是有c/c++函数库提供的,机制复杂,需要一些列内存,合并内存和释放内存的方法,因此效率低
-
c++中内存布局
堆,栈,自由存储区,全局/静态存储区,常量存储区
https://www.cnblogs.com/QG-whz/p/5060894.html
堆和自由存储区的区别是,new所申请的内存区域在C++中称为自由存储区,堆是操作系统维护的一块内存,而自由存储区是C++通过new和delete动态分配和释放对象的抽象概念,堆和自由存储区并不等价。
-
链表和数组的区别
数组:元素在内存中连续存放,可以通过下标迅速访问数组中任何数组,插入和删除的效率低,随机读取的效率高,可能会浪费内存。
链表:在内存中不是顺序存储的,内存利用率高,插入删除速度快,但是不能随机查找
-
排序算法及其复杂度
-
冒泡排序
从数组中的第一个数开始,依次遍历数组中的每一个数,通过相邻比较兑换,找出剩余的没排序的数中最大(小)的移动至数列的前端 稳定, n^2
-
插入排序
从第二个数开始,依次和前面的记录相比,寻找合适的位置 稳定,n^2
-
希尔排序
先将整个待排列记录分割成若干子序列,然后分别进行直接插入排序,等到整个序列中的记录基本有序的时候,对全体进行一次插入排序,不稳定,nlogn
-
选择排序
从所有记录中选出最小的一个数据元素与第一个位置的记录交换;然后在剩下的记录当中再找最小的与第二个位置的记录交换,循环到只剩下最后一个数据元素为止。 不稳定,n^2
-
快速排序
不稳定,nlogn
-
堆排序
稳定,nlogn
-
归并排序
分治法,假设当前有m个元素,看成m个长度为1的子序列, 然后两两合并,得到n/2个长度为2或者1的子序列,然后再两两合并
复杂度nlogn,稳定
稳定,nlogn
-
计数排序
如果比元素x小的元素个数有n个,则元素x排序后位置为n+1,不稳定,:O(n+k),k是待排序数的范围。
-
桶排序
稳定, o(n + c),c为桶内排序时间
Mysql整理
面试问题
-
排序的规则
排序采用order by子句,order by后面加上排序的字段,如果有where的话要放在where的后面
升序 asc
降序 desc
-
函数的分类,经常使用的函数
字符串函数(mid,ltrim,left, instr,char_length, group_concat),时间函数(now,curdate,curtime,datediff,date_add,unix_stamp,form_unixtime),数学函数(ceil,max, min,round,sum,avg)
聚合函数,分组函数
-
分组查询注意条件
如果使用了order by,order by必须放到group by后面;如果想要对分组数据再进行过滤需要使用having子句
-
limit使用方法
limit m,n 代表从m + 1 开始,展示n条数据
-
创建表时有什么约束条件?
字段名,字段数据类型,字段长度限制,字段约束
-
mysql常见的数据类型
char,varchar, double,int/bigint/tinyint, date
-
常见约束
非空not null, 唯一约束 unique, 主键约束 primary key, 外键约束 foreign key
-
主键外键的区别?
主键唯一标识一条记录,不能有重读,不能为空
外键 当前表的外键是另一个表的主键,外键可以为空,可以重复
-
mysql和oracle的区别?
oracle的语法更加严谨
mysql有特殊的存储引擎,而oracle没有
-
什么是存储过程?用什么来调用?
允许模块化设计的SQL语句集,只要创建一次这个程序就可以调用多次
调动方法, call
-
char和varchar的区别?
char是一种固定长度的字符类型,varchar是一种可变长度的类型
-
存储日期?
date函数,datetime函数,timestamp函数
-
DDL语句和DML语句的区别?
DDL ,data definition language 数据定义语言,用来维护数据库对象,对数据库内部的对象进行创建,删除,修改的操作语言
常用的语句关键字主要包括 create, drop, alter等
DML,data manipulation language 数据操纵语言,常用的语句关键字主要包括 insert,delete,update和select等
-
drop, delete与truncate的区别?
drop直接删掉表和表的结构。
delete删除表中的数据。
truncate删除表中数据,再插入的时候自正常id又从1开始,不删除表结构
-
左连接和右连接的区别
左连接能查询到的数据,右连接同样可以完成
左连接是显示左表的所有数据,没有匹配的以null显示,右连接相反
左连接只影响右表,右连接只影响左表
-
数据库设计三范式?
第一范式:数据库表的每一列都是不可分割的原子数据项
第二范式:非主键字段都和主键相关,而不能只和主键的某一部分相关(主要针对联合主键而言)
第三范式:数据表中的每一列数据都和主键直接相关,而不能间接相关
数据安全
-
了解XSS攻击吗?如何防止?
XSS(cross site scripting),跨站脚本攻击,是一中在web应用中的计算机安全漏洞,允许恶意的web用户将代码植入到提供给其他用户的页面中,臊面的js的内容,进行脚本添加攻击
危害:
-
盗取各类用户账号
-
控制企业数据
防止:
基于代码修改的防御
-
对用户提交的内容进行可靠的输入验证,限制长度或者格式
-
session标记
-
去掉css和js的引用内容
-
SQL注入漏洞产生的原因?如何防止
原因:
通过设定固定的参数来传参给服务器,查询出一些重要的信息。主要原因是没有细致的过滤用户输入的主要内容,导致非法数据侵入系统
产生方式:
-
通过表单动态拼接sql
-
通过拼接 / 1=1等语句获取信息
如何预防:
-
对表单进行验证
-
对单引号和双引号进行格式转换
-
对用户名密码进行加密
-
每个应用赋值的表建立自己的权限
-
项目上线前进行sql注入检测
事务
-
什么是事务?
访问并更新数据库中各项数据项的一个程序执行单元,由高级数据库操纵语言或编程语言书写的用户程序的执行引起
-
数据事务库的特性
-
原子性:整个数据库的操作,要么全部完成,要么全部不完成
-
一致性:必须是从一个状态到另一个状态,要么所有的操作一次性操作,要么就是不定
-
隔离性:并发的事务不会互相干扰
-
持久性:一个事务一旦提交,对该数据库中这个表的操作是永久存在的
-
设置手动提交
默认是no,将autocommit的值设置为off
索引
22, 索引的目的?
-
快速访问数据表中的特定信息,提高检索速度
-
创建唯一性索引,保证数据库中每一行数据的唯一性
-
加速表和表之间的链接
-
使用分组和排序自己进行数据检索的时候,可以减少查询中分组和排序的时间
-
什么时候使用索引?
-
表中该字段的数据量庞大
-
经常被检索,经常出现在where子句中的字段
-
经常被DML的字段不建议添加索引
-
索引对数据库系统负面的影响?
-
创建索引和维护索引需要耗费时间
-
索引需要占用物理空间
-
当对表进行增,删,改,查的时候也需要动态维护,降低了数据的维护速度
-
索引的使用
create index 索引名 on 表名(字段名);
-
sql使用过程和优化小方法
-
避免在where中使用 != 或 <> ,否则引擎将放弃使用索引而进行全表扫描
-
避免在where子句中对字段进行null值判断 ,否则引擎将放弃使用索引而进行全表扫描(可以在null上设置默认值0,避免null)
-
用exists 代替in是一个好选择,因为in不走索引
-
什么是锁?
数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性
基本锁的类型:行级锁和表级锁
-
对class的理解,面向对象的三个特点
-
如果将私有成员变量取出
-
*this指针
-
类和对象的区别和联系
-
值拷贝&&内容拷贝 浅拷贝&&深拷贝
-
拷贝构造函数和赋值函数的区别
-
类中析构函数和构造函数调用顺序
-
虚函数,纯虚函数
-
=default 和=delete
-
explicit关键词
-
new/delete, new[]/delete[]
-
new和malloc的区别
-
vector的底层实现
-
allocator的作用
-
智能指针
-
initializer_list
-
const和constexpr
-
static_assert
-
引用和指针
-
将引用作为函数参数有哪些特点
-
在什么时候需要使用“常引用”
-
将引用作为函数返回值类型的好处和需要遵循的规则
-
struct和union的区别
-
重载和重写的区别
-
什么时候只能用intialization list而不能用assignment
-
描述内存分配方式和他们的区别
-
const和define相比的优点?
-
数组和指针的区别
-
基类的析构函数不是虚函数,会带来什么问题?
-
全局变量和局部变量的区别?
-
static关键词作用
-
抽象和接口
-
进程和程序的区别
-
类的静态成员和非静态成员有何区别
-
extern关键词
-
extern和static的区别
-
c++中的四种转换
-
野指针
-
智能指针的内存泄漏问题如何解决?
-
为什么析构函数必须是虚函数
-
为什么c++默许的析构函数不是虚函数?
-
函数指针
-
c++ 中析构函数
-
c++定义常量的方法,常量存放在哪个位置,const的作用
-
c++函数栈空间最大值
-
虚函数表实现机制
-
c语言是如何进行函数调用的
-
c++是如何处理返回值的
-
为何map和set的插入删除效率比其他序列容器高?
-
为何map和set每次insert之后,以前保存的iterator 不会失效
-
vector在进行删减操作的时候,指针为什么会失效?
-
STL的基本组成和STL中的allocator
-
当数据增多的时候,map和set的插入和搜索速度变化?
-
为何直接使用定义的库函数还是不如封装的效果好?
-
map和set的区别,分别是如何实现的?
-
STL迭代器删除元素
-
map和unordermap
-
map和multimap
-
vector和list的区别
-
STL中的迭代器的作用和指针的作用
-
STL中的resize和reserve的区别
-
c++中类成员的访问权限
-
c++中struct和class的区别
-
左值和右值的概念
-
什么是右值引用,和左值引用有什么区别?
-
malloc的原理
-
什么是内存泄漏,如何判断
-
内存泄漏分类
-
红黑树和AVL树的定义,特点,以及两者之间的区别
-
哈夫曼编码
-
map底层为什么要用红黑树
-
B树,B+树
-
map和unordered_map的底层实现
-
epoll是如何实现的
-
stack overflow
-
栈和堆的区别,为什么栈要比堆块?
-
c++中内存布局
-
链表和数组的区别
-
排序算法及其实现
-
什么是稳定排序
-