2018秋招面试问题(三、C++基础问题)

注:面试过程中整理的学习资料,如有侵权联系我即刻删除。

目录

C++中expicit的用法

new和maloc的区别?

new运算符的原理

new的重载

堆和栈的区别?为什么栈比堆快?

函数调用的压栈和出栈过程

函数调用时参数的压栈顺序为什么是从右到左?

数组和vector相比,使用上如何选择?

顺序容器有哪些?

关联容器有哪些?

C++中局部变量和全局变量可以重名吗?

reease和debug的区别

C++中static的作用

类与类的关系有哪些

如何画一个类图

关于类成员的访问权限问题


C++中expicit的用法

explicit用来修饰类的构造函数(用在类内部的构造函数的声明上),被修饰的构造函数的类,不能发生隐式类型转换,只能以显示的方式做类型转换。

加了explicit之后,就不能用=来赋了,只能用s = A temp(10);

new和maloc的区别?

1.new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。

2.使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的大小。

p = (int *)malloc(100 * sizeof(int));

p = new int;

3.new一个对象的时候在申请内存的时候会调用对象的构造函数,而malloc只会申请内存;同样,delete在释放内存之前,会调用析构函数,而free只是释放内存。

4.new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

5.new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回null。

6.C++允许new/delete重载,不允许malloc重载。

//new操作符从自由存储区(free store)上为对象动态分配内存空//间,而malloc函数从堆上动态分配内存。自由存储区不等于堆。

new运算符的原理

new的内部实现分为两步:分配内存和调用构造函数初始化

内存分配

调用相应的 operator new(size_t) 函数,动态分配内存。如果 operator new(size_t) 不能成功获得内存,则调用 new_handler() 函数用于处理new失败问题。如果没有设置 new_handler() 函数或者 new_handler() 未能分配足够内存,则抛出 std::bad_alloc 异常

< “new运算符”所调用的 operator new(size_t) 函数,按照C++的名字查找规则,首先做依赖于实参的名字查找(即ADL规则),在要申请内存的数据类型T的 内部(成员函数)、数据类型T定义处的命名空间查找;如果没有查找到,则直接调用全局的 ::operator new(size_t) 函数。>

构造函数:

在分配到的动态内存块上初始化相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用 operator delete(void*, void*) 函数释放已经分配到的内存。

delete的内部实现:

首先调用相应类的析构函数,然后再调用operator delete函数。

operator new内部是用的malloc,operator delete内部是用free。

new的重载

new重载其实重载的是内部的operator new函数以及operator delete函数。operator new的第一个参数必须是size_t,也就是无符号整型,返回值得是void*。operator delete的参数必须是void*,当然还可以有别的参数。

堆和栈的区别?为什么栈比堆快?

(1)栈是程序编译时系统自动分配空间,而堆是运行时程序员手动分配空间。

(2)堆在分配和释放时都要调用函数(malloc/free),分配时会去堆空间寻找足够大小的空间,这些都会花费一定的时间,而栈不会。

(3)访问栈是直接访问的,而访问堆是间接寻址的,第一次取得指针,第二次才能取出值。

栈是自顶向下的,栈顶在低地址,栈底在高地址。

函数调用的压栈和出栈过程

压栈:在函数调用时,第一个进栈的是函数的各个参数,然后是主函数中函数调用后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数中的局部变量。注意静态变量是不入栈的。

出栈:就是和压栈反着的一个过程。

函数调用时参数的压栈顺序为什么是从右到左?

这主要是因为有不定长参数的函数,为了支持可变长参数,比如printf(const char* format,…),实际应用中它的参数个数是由format中的%占位符的个数来决定的,如果压栈是从左到右的话,format先进栈,上面压着未知个数的参数,这样就无法知道参数的个数了,所以要从右到左进栈,让format挨着栈顶。

数组和vector相比,使用上如何选择?

vector是变长数组,有很多现成的函数可以使用,比如判空、返回长度等,还可以动态添加删除数据,但是内存和计算的开销更大些。数组是定长的,执行效率更高一些,访问效率很高。如果我们知道数组的总长,并且不用随时增加删除元素,那就可以选择数组,如果不定长,且需要随时增加删除元素,那就选择vector更加方便。

顺序容器有哪些?

顺序容器就是指各容器之间有顺序关系的线性表。

deque、list、vector。其实就是线性表。每个元素都有固定位置,位置由添加进去的次序决定的。List是双向链表。

vector、deque与list的区别是,vector、deque内存是连续的,而list是不连续的,list不能用下标访问元素,vector、deque的查询很方便,而list的插入和删除很方便。vector和deque的区别是deque可以支持首元素的删除插入(也支持尾元素),而vector只能插入删除尾部。

关联容器有哪些?

关联容器的底层是非线性的红黑树结构,默认插入的时候是按键值key升序排列。

map、set、multi_map、multi_set。map是key-value模式,是一个key对应一个值,set是单值模式,单值其实就是set的key与值相等(set的value之间不可以相等),并且set值是不可以修改的。multi_map就是一个key可以对应多个value,multi_set就是value之间可以相等。

C++中局部变量和全局变量可以重名吗?

重名是看作用域

可以。局部声明的作用域是从声明的那一点开始,直到这个声明所在的那个块结束为止。全局变量的作用域是从声明的那一点开始,直到这个声明所在的文件的结束为止。与全局变量重名的局部变量在块内可以屏蔽全局变量,如果想在块内使用全局变量可以通过作用域解析符::来引用。

reease和debug的区别

一组编译选项的不同。

Debug版:
经过编译器编译出的项目.exe文件大,而且生成的二进制命令没有经过编译器的优化。项目中包含着丰富的调试信息,供programer调试程序。这就是为什么,当我们在Debug程序的时候,为什么程序就会在我们设置断电的地方自动停下,而且仿佛时间静止,还可以显示此时相关变量的状态。

Release版:
这个版本是的出发点是用户,所以不保存调试信息,编译器在编译的时候进行了各种优化,进而达到,代码文件最小执行速度最优

优化了以下:

(1)变量,Debug版如果你不初始化变量,变量自动初始化为0xCC,删除动态分配的内存时将其赋值为0xCD。Release版不会自动对变量初始化,删除动态分配的内存时也不对内存中的数据进行处理。
(2)内存分配的长度,debug版以32bytes为单位分配,release版以8bytes为单位。比如你定义int a[4]; 在Debug版里分配的内存长度是32byte,而在Release版里则分配16byte。所以在debug版里如果你定义a[4],却在程序里使用a[4] = xxx不会出问题,但是在release版里就会出问题.
(3)变量的类型,Release版里对于经常使用的变量会自动使其变成寄存器变量,以加快程序运行速度。

C++中static的作用

static最重要的作用是隐藏,对于全局变量来说,所有未加static前缀的全局变量和函数都具有全局可见性,加extern其它的源文件也能访问。如果加了static,就会对别的源文件隐藏,利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。

对于局部变量来说,加了static延长了生命期。如果一个函数经常被调用,我们希望在下一次调用之前可以保存其中一个局部变量的值,那么就可以加static把它变为静态局部变量。全局当然也可以,但是没必要,破化了访问范围。

类与类的关系有哪些

纵向
继承:一个类继承另外的一个类的功能,并可以增加它自己的新功能的能力(空心三角箭头 子类指向父类 )
实现:一个类实现一个接口(空心三角箭头,类指向接口)
横向:
依赖 :一个类使用到另一个类。关系是临时的偶然的,但类B的变化会影响到A(虚线箭头)。代码中就是类B作为函数参数被类A在某个方法当中使用。
关联:两个类关系是长期的,双方平等的(实线箭头),比如我和朋友。(可能有引用对方一个全局变量之类的)
聚合:是关联关系的一种特例。就看它们之间是不是整体与部分,一个属于另外一个的关系。可以分离,有各自的生命周期,部分可以属于多个整体,也可以为多个整体共享(空心菱形加实线箭头),比如公司和员工。
组合:也是关联关系的一种特例。也是整体与部分关系,不可分,整体的生命周期结束,部分的也结束(实心菱形加实线箭头),比如人和人的大脑。

如何画一个类图

用Rational Rose工具来画uml类图,首先创建类,创建类的名字属性、方法,然后再创建类与类之间的关系,关系在工具栏中有显示。

每个类都有三个基本元素:类名、属性、方法

根据类与类的关系来画一个类图。

虚线箭头指向依赖;实线箭头指向关联;虚线三角指向接口;实线三角指向父类;空心菱形能分离而独立存在,是聚合;实心菱形精密关联不可分,是组合;

关于类成员的访问权限问题

< 私有成员只允许本类的成员函数和友元函数访问,类外部的任何访问都是非法的。Protected成员在没有继承的时候访问权限与private相同,在有继承的时候,派生类可以访问父类的protected成员。>

  • 派生类的sizeof大小,无论是何种方式继承,都是基类中的成员变量之和加上虚函数再加上派生类中定义的变量,不管派生类中定义的是与基类同名的变量还是不同名的,都要加上这部分。派生类会隐藏基类同名的变量,但是空间却各是各的
  • 无论何种继承方式,派生类中无法访问基类的private成员,却一直都能访问protected成员和public成员。
  • 在main函数中,一直无法访问private成员变量,也无法访问protected成员。只有继承之后仍是public成员的,main函数才能访问。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值