🍎 博客主页:🌙@披星戴月的贾维斯
🍎 欢迎关注:👍点赞🍃收藏🔥留言
🍇系列专栏:🌙 C/C++专栏
🌙请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信,世界上总有一些美好值得我们全力以赴,哪怕粉身碎骨!🌙
🍉一起加油,去追寻、去成为更好的自己!
文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
前言
前天,我们总结了C++面经的一部分,我也参加了某平台的模拟面试,对于面试的要点颇有心得,希望这次能和大家继续聊聊C++面经这个话题,这是对自己学过的知识的进行的总结,也希望能对大家有所帮助!
🍎1、参加模拟面试的心得
首先,有一个扎实的基础是一张王牌,所以我们要努力学习,少偷懒。面试会考的几个点:C/C++的基础知识, 网络部分,Linux基础,操作系统,算法。这5个点考察的都会比较深,我们一定要理解充分这些要点,如果在面试中遇到不会的点,大大方方和面试官说,不要不懂装懂。C/C++这块常问智能指针,new/delete,面向对象三大特性,特别是多态这个要点;网络部分重中之中是tcp/udp,http/https,tcp的三次握手等等,Linux基础知识可能会考一些基础操作,解压缩,管道之类的,操作系统部分可能考进程,线程,锁,虚拟内存等等,算法可能考排序,DFS,动态规划,贪心,等等。
🍎2、介绍C++所有的构造函数
C++构造函数主要有默认构造、重载构造函数和拷贝构造函数。
默认构造是当类没有实现自己的构造函数的时候,编译器默认提供的一个构造函数。
重载构造函数也被称为一般构造函数,一个类可以有多个重载构造函数,但是需要他们的参数列表不相同。
拷贝构造函数是在发生对象赋值的时候调用的,其参数是一个对象,并且必须是引用或者指针,不能是对象本身。
🍇什么情况下会调用拷贝构造函数?
-
1.对象以值传递的方式传入函数参数。
-
2.对象以值传递的方式从函数返回,会拷贝返回一个临时对象,然后用临时对象去给外面接收的对象赋值。
-
3.对象需要调用另一个对象进行初始化。这个和第一条有点像。
🍎3、结构体内存对齐方式和为什么要进行内存对齐
因为结构体的成员可以有不同类型的数据,这些数据所占的内存空间大小也不一样。同时,CPU又是按块读取内存的,内存对齐可以让CPU一次就将需要的数据独取出来。否则,CPU就需要多次多去了。
内存对齐的规则:
- 第一个成员在结构体中的偏移量为0
- 其他成员变量要按照对齐数的整数倍地址存放
- 对齐数=min(编译器默认对齐数,该成员对齐数),在Linux中是4,vs中是8
- 结构体的总大小为最大对齐数的整数倍
🍎4、C++的智能指针(重点)
C++的智能指针有auto_ptr,shared_ptr,weak_ptr和unique_ptr。这四种只能指针都是通过RAII机制(利用对象生命周期来控制程序资源)。也就是说智能指针是一个“对象”,利用智能指针“对象”的生命周期,来控制管理的资源,当智能指针生命周期到了以后,就会在析构函数中delete掉删除的对象。
- auto_ptr是比较早的智能指针,在进行指针拷贝和赋值的时候,新指针会直接接管旧指针的资源,也就是说,旧指针会直接被置为nullptr,后续如果需要访问旧指针的资源时就会出现问题,可能会出现同一块空间析构两次的尴尬场面。
- unique_ptr是auto_ptr的改良版,不能够拷贝也不能赋值,保证一个对象对应一个指针。
- shared_ptr采用引用计数,使得一个对象可以有多个智能指针,当计数为0时说明该资源已经没有指针指向了,可以释放资源了。但是shared_ptr如果作为类的成员变量的话,容易出现循环引用的问题。比如类Obj中有一个指向自己的shared_ptr指针,两个对象a和b,他们的shared_ptr互相指向对方,这就会出现循环引用的问题。
- weak_ptr是为了解决shared_ptr而出现的,他没有RAII机制,没有引用计数。在使用weak_ptr指向的对象前,需要确定这个对象有没有被释放,此时可以调用weak_ptr的clock()成员函数来将其提升成shared_ptr指针,如果对象被释放了,此次提升就会返回nullptr。
特别注意:shared_ptr不是线程安全的,它只保证引用计数是线程安全的,并不能保证管理对象的读写是线程安全的。
unique_ptr不能进行拷贝,那如何进行资源转移呢?
unique_ptr不允许对左值进行拷贝,但是支持对右值资源的转移。
🍎5、模板的用法与适用场景 实现原理
模板是用template关键字进行声明。
编译器会对函数编译器模板进行两次编译:
- 在声明的地方对模板代码本身进行编译,这次编译只是会进行语法检查,并不会生成具体代码。
- 第二次编译则是在函数调用时根据模板的类型参数列表具体的实现这个模板对应的类型的函数实例。
🍎6、知道C++11新特性吗?
- 自动类型推导auto:auto声明的变量必须马上初始化,编译器根据初始化的值推导出它的类型。
- nullptr:nullptr关键字是为了解决NULL的二义性问题而引进的新的类型,因为在C++中NULL实际上代表的是0,在C语言中是void * 的指针,因为C++不能把void * 类型的指针隐式转化成其他类型的指针,为了避免二义性则将其改为了0。而nullptr没有实际的类型名称,所以可以用来表示空指针。
- lambda表达式:lambda表达式会自动被编译器处理成一个仿函数并重载()。
- thread线程库和mutex锁
- 智能指针
🍎7、快排的递归和非递归实现
🍇快排的递归实现
int PartSort(int* a, int left, int right)
{
int keyi = left;//key设置成最左边的数
while (left < right)
{
//右边找小
while (left < right && a[right] >= a[keyi])
--right;
//左边找大
while (left < right && a[left] > a[keyi])//找大
++left;
Swap(&a[left], &a[right]);
}
Swap(&a[keyi], &a[left]);
return left;
}
void QuickSort(int* a, int begin, int end)
{
//子区间相等只有一个值或者不存在那么就是递归结束的子问题
if (begin >= end)
return;
int keyi = PartSort(a, begin, end);
// [begin, keyi - 1]keyi[keyi + 1, end]
QuickSort(a, begin, keyi - 1);
QuickSort(a, keyi + 1, end);
}
🍇快排的非递归实现(依靠栈)
快排的非递归写法
void QuickSort5(int* a, int begin, int end)
{
ST st;
StackInit(&st);
//入栈
StackPush(&st, begin);
StackPush(&st, end);
//栈是后进先出
while (!StackEmpty(&st))
{
int right = StackTop(&st);
StackPop(&st);
int left = StackTop(&st);
StackPop(&st);
int keyi = PartSort3(a, left, right);
//[left, keyi - 1][keyi + 1, right]
if (left < keyi - 1)//还要继续入栈的条件
{
StackPush(&st, left);
StackPush(&st, keyi - 1);
}
if (keyi + 1 < right)
{
StackPush(&st, keyi + 1);
StackPush(&st, right);
}
}
StackDestory(&st);
}
PartSort3
//前后指针法
int PartSort3(int* a, int left, int right)
{
int mini = Getmini(a, left, right);
Swap(&a[mini], &a[left]);
int keyi = left;//如果是a[left],则是局部变量,SWap后还是原来的值
//left则是下标
int prev = left, cur = left + 1;
while (cur <= right)
{
if (a[cur] < a[keyi] && a[++prev] != a[cur])
Swap(&a[prev], &a[cur]);
++cur;
}
Swap(&a[prev], &a[keyi]);
return prev;
}
🍎总结
本文向大家介绍了几个C/C++面试中可能会被问到的问题,以及自己的一些面试心得,希望对读者能有所帮助!