首页 > 《C++面试宝典》V1.0 冲刺大厂~持续更新(6)
头像
许嵩不爱吃土豆鸭
编辑于 2021-08-05 14:44
+ 关注

《C++面试宝典》V1.0 冲刺大厂~持续更新(6)

分享面试总结,涉及C++、算法、数据结构、操作系统、计算机网络、Linux、数据库、设计模式等后面持续更新~

内容多为一问一答式,多数来自收集。整理总结,视频、书籍学习所得,如有错误请指出,万分感谢!!!
学习建议:针对八股文,不太了解的可以网上扩展,自己总结,拿来主义最好能消化成自己的。
※代表高频问题(参考)

# C++篇---  √6

101. 数组和指针的区别?

1) 数组在内存中是连续存放的,开辟一块连续的内存空间;数组所占存储空间:sizeof(数组名);数组大小:sizeof(数组名)/sizeof(数组元素数据类型);
2) 用运算符sizeof可以计算出数组的容量(字节数)。sizeof(p)其中p为指针得到的是一个指针变量的字节数,而不是p所指的内存容量。
3) 编译器为了简化对数组的支持,实际上是利用指针实现了对数组的支持。具体来说,就是将表达式中的数组元素引用转换为指针加偏移量的引用。
4) 在向函数传递参数的时候,如果实参是一个数组,那用于接受的形参为对应的指针。也就是传递过去是数组的首地址而不是整个数组,能够提高效率。
5) 在使用下标的时候,两者的用法相同,都是原地址加上下标值,不过数组的原地址就是数组首元素的地址是固定的,指针的原地址就不是固定的。


102. C++如何阻止一个类被实例化?一般在什么时候将构造函数声明为private?

将类定义为抽象基类或将构造函数声明为private。
不允许类外部创建类对象,只能在类内部创建对象。


103. 如何禁止自动生成拷贝构造函数?

1) 为了阻止编译器默认生成拷贝构造函数和拷贝赋值函数,我们需要手动去重写这两个函数,某些情况下,为了避免调用拷贝构造函数和拷贝赋值函数,我们需要将他们设置成private,防止被调用。
2) 类的成员函数和friend函数还是可以调用private函数,如果这个private函数只声明不定义,则会产生一个连接错误。
3) 针对上述两种情况,我们可以定一个base类,在base类中将拷贝构造函数和拷贝赋值函数设置成private,那么派生类中编译器将不会自动生成这两个函数,且由于base类中该函数是私有的,因此,派生类将阻止编译器执行相关的操作。


104. assert与NDEBUGE?

1) assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
#include <assert.h>
void assert( int expression);
assert的作用是现计算表达式 expression,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用abort来终止程序运行。如果表达式为真,assert什么也不做。
2) NDEBUG宏是Standard C中定义的宏,专门用来控制assert()的行为。如果定义了这个宏,则assert不会起作用。定义NDEBUG能避免检查各种条件所需的运行时开销,此时不会执行运行时检查。
3) C Standard中规定了assert以宏来实现。<assert.h>被设计来可以被多次包含,其中一上来就undef assert,然后由NDEBUG宏来决定其行为。

105. Denug和release的区别?

1) 调试版本,包含调试信息,所以容量比Release大很多,并且不进行任何优化(优化会使调试复杂化,因为源代码和生成的指令间关系会更复杂),便于程序员调试。Debug模式下生成两个文件,除了.exe或.dll文件外,还有一个.pdb文件,该文件记录了代码中断点等调试信息;
2) 发布版本,不对源代码进行调试,编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的。(调试信息可在单独的PDB文件中生成)。Release模式下生成一个文件.exe或.dll文件。
3) 实际上,Debug和Release并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。

106. 虚函数与纯虚函数区别?

1) 纯虚函数只有定义没有实现,虚函数既有定义又有实现。
2) 含有纯虚函数的类不能定义对象,含有虚函数的类能定义对象。
请网上自行查阅!

107. 写一个比较大小的模板函数?

#include< iostream>
using namespace std;
template<typename type1, typename type2>//函数模板
type1 Max(type1 a, type2 b)
{
return a > b ? a : b;
}
void main()
{
cout << "Max = " << Max(5.5,'a') << endl;
}

108. 智能指针怎么用?※智能指针出现循环引用怎么解决?

1) shared_ptr
调用一个名为make_shared的标准库函数,shared_ptr<int> p = make_shared<int>(42);通常用auto更方便,auto p = …;shared_ptr<int> p2(new int(2));
每个shared_ptr都有一个关联的计数器,通常称为引用计数,一旦一个shared_ptr的计数器变为0,它就会自动释放自己所管理的对象;shared_ptr的析构函数就会递减它所指的对象的引用计数。如果引用计数变为0,shared_ptr的析构函数就会销毁对象,并释放它占用的内存。

2) unique_ptr
一个unique_ptr拥有它所指向的对象。某个时刻只能有一个unique_ptr指向一个给定对象。当unique_ptr被销毁时,它所指向的对象也被销毁。

3) weak_ptr
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。

4) 弱指针用于专门解决shared_ptr循环引用的问题,weak_ptr不会修改引用计数,即其存在与否并不影响对象的引用计数器。循环引用就是:两个对象互相使用一个shared_ptr成员变量指向对方。弱引用并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

109. strcpy函数和strncpy函数的区别?哪个函数更安全?
1) 函数原型
char* strcpy(char* strDest, const char* strSrc)
char* strncpy(char* strDest, const char* strSrc, int pos)

2) strcpy函数: 如果参数 dest 所指的内存空间不够大,可能会造成缓冲溢出(buffer Overflow)的错误情况,在编写程序时请特别留意,或者用strncpy()来取代。
strncpy函数:用来复制源字符串的前n个字符,src 和 dest 所指的内存区域不能重叠,且 dest 必须有足够的空间放置n个字符。

3) 如果目标长>指定长>源长,则将源长全部拷贝到目标长,自动加上’\0’
如果指定长<源长,则将源长中按指定长度拷贝到目标字符串,不包括’\0’
如果指定长>目标长,运行时错误。

110. 成员函数里memset(this,0,sizeof(*this))会发生什么?

1) 有时候类里面定义了很多int,char,struct等c语言里的那些类型的变量,我习惯在构造函数中将它们初始化为0,但是一句句的写太麻烦,所以直接就memset(this, 0, sizeof *this);将整个对象的内存全部置为0。对于这种情形可以很好的工作,但是下面几种情形是不可以这么使用的;
2) 类含有虚函数表:这么做会破坏虚函数表,后续对虚函数的调用都将出现异常;
3) 类中含有C++类型的对象:例如,类中定义了一个list的对象,由于在构造函数体的代码执行之前就对list对象完成了初始化,假设list在它的构造函数里分配了内存,那么我们这么一做就破坏了list对象的内存。


111. 方法调用的原理(栈,汇编)?

1) 机器用栈来传递过程参数、存储返回信息、保存寄存器用于以后恢复,以及本地存储。而为单个过程分配的那部分栈称为帧栈;帧栈可以认为是程序栈的一段,它有两个端点,一个标识起始地址,一个标识着结束地址,两个指针结束地址指针esp,开始地址指针ebp;

2) 由一系列栈帧构成,这些栈帧对应一个过程,而且每一个栈指针+4的位置存储函数返回地址;每一个栈帧都建立在调用者的下方,当被调用者执行完毕时,这一段栈帧会被释放。由于栈帧是向地址递减的方向延伸,因此如果我们将栈指针减去一定的值,就相当于给栈帧分配了一定空间的内存。如果将栈指针加上一定的值,也就是向上移动,那么就相当于压缩了栈帧的长度,也就是说内存被释放了。

3) 过程实现:
备份原来的帧指针,调整当前的栈帧指针到栈指针位置;
建立起来的栈帧就是为被调用者准备的,当被调用者使用栈帧时,需要给临时变量分配预留内存;
使用建立好的栈帧,比如读取和写入,一般使用mov,push以及pop指令等等。
恢复被调用者寄存器当中的值,这一过程其实是从栈帧中将备份的值再恢复到寄存器,不过此时这些值可能已经不在栈顶了
恢复被调用者寄存器当中的值,这一过程其实是从栈帧中将备份的值再恢复到寄存器,不过此时这些值可能已经不在栈顶了。
释放被调用者的栈帧,释放就意味着将栈指针加大,而具体的做法一般是直接将栈指针指向帧指针,因此会采用类似下面的汇编代码处理。
恢复调用者的栈帧,恢复其实就是调整栈帧两端,使得当前栈帧的区域又回到了原始的位置。
弹出返回地址,跳出当前过程,继续执行调用者的代码。

4) 过程调用和返回指令:
call指令
leave指令
ret指令

112. 回调函数的作用?

1) 当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数;
2) 回调函数就相当于一个中断处理函数,由系统在符合你设定的条件时自动调用。为此,你需要做三件事:1,声明;2,定义;3,设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用;
3) 回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数;
4) 因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。

113. C++中常见数据类型转换?





其余,请网上自行查询扩展!面试的过程中多吃涉及字符串转化,数据转化等问题,请认真的学习准备这方面知识储备。

C++部分,后期持续更新!!!

未完待续~

需资料分享,可私聊哈。


全部评论

(4) 回帖
加载中...
话题 回帖

推荐话题

相关热帖

近期热帖

近期精华帖

热门推荐