文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

我们一起聊聊硬钢百度面试!

2024-11-30 16:41

关注

今天分享一位百度春招面经,读者的技术栈是C++。

这次的面经,主要都是问操作系统、网络编程、C++ 这三大方向。

能明显感觉到,C++面试和Java或者Go面试重点,Java/Go主要是问MySQL、Redis。

一、介绍一下webserver项目

  1. 服务器开始运行,创建(初始化)线程池(IO密集型,线程数n+1);
  2. 创建 epoll 对连接进行监听
  3. 监听到连接事件,调用线程池线程处理 http 请求
  4. 读取 http 请求并对其进行解析 (空格,\r\n字段提取)
  5. 返回解析结果

二、select、poll、epoll的选择

select缺点:

  1. select() 检测数量有限制,最大值通常为 1024(bit),每一个比特位对应一个监听的文件描述符
  2. fd_set被内核修改后,不可以重用,每次都需要重置
  3. 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  4. 每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大(((时间复杂度是O(n))))

poll缺点:select第三四条缺点没有解决

  1. 每次调用select,都需要把**fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  2. 每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大(((时间复杂度是O(n))))

epoll优点:epoll底层数据结构

三、线程和进程的区别?使用线程的心得?

  1. 进程是资源(包括内存、打开的文件等)分配的单位,线程是 CPU 调度的单位;
  2. (关键词:进程独立空间、线程之前共享空间资源)进程拥有一个独立完整的资源平台,不和其他进程共享;而线程只独享必不可少的资源,如寄存器和栈,而一个进程里可以有多个线程,彼此共享同一个地址空间。
  3. 线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系;
  4. 线程能减少并发执行的时间和空间开销

对于,线程相比进程能减少开销,体现在:

所以,不管是时间效率,还是空间效率线程比进程都要高

心得:线程使用有一定难度,需要处理数据一致性问题,比如要使用互斥锁和条件变量等同步机制保证线程安全(原子性操作)

四、C++ 空类的大小?一个只包含int 变量的空class和只包含int变量的空struct的内存各占多大?

关键词:空类和空结构体都大小为1,这样可以确保两个不同的对象,拥有不同的地址。

1.空类

class A {};
int main(){
cout<<sizeof(A)<<endl;// 输出 1;
A a;
cout<<sizeof(a)<<endl;// 输出 1;
return 0;
}
  1. C++空类的大小不为0,不同编译器设置不一样,vs和lg++都是设置为1;
  2. C++标准指出,不允许一个对象(当然包括类对象)的大小为0,不同的对象不能具有相同的地址;
  3. 带有虚函数的C++类大小不为1,因为每一个对象会有一个vptr指向虚函数表,具体大小根据指针大小确定;
  4. C++中要求对于类的每个实例都必须有独一无二的地址,那么编译器自动为空类分配一个字节大小,这样便保证了每个实例均有独一无二的内存地址。

在C++中空类会占一个字节,这是为了让对象的实例能够相互区别。具体来说,空类同样可以被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加上一个字节,这样空类实例化之后就会拥有独一无二的内存地址。当该空白类作为基类时,该类的大小就优化为0了,子类的大小就是子类本身的大小。这就是所谓的空白基类最优化。

空类的实例大小就是类的大小,所以sizeof(a)=1字节**,如果a是指针,则sizeof(a)就是指针的大小,即4字节。**

2.含有虚函数的类的大小

class A { virtual Fun(){} };
int main(){
cout<<sizeof(A)<<endl;// 输出 4(32位机器)/8(64位机器);
A a;
cout<<sizeof(a)<<endl;// 输出 4(32位机器)/8(64位机器);
return 0;
}

因为有虚函数的类对象中都有一个虚函数表指针 __vptr,其大小是4字节

3.只含有一个int成员变量的类的大小(4)

class A { int a; };
int main(){
cout<<sizeof(A)<<endl;// 输出 4;
A a;
cout<<sizeof(a)<<endl;// 输出 4;
return 0;
}

只是一个int变量的大小——4字节

4.只含有一个静态成员变量的类的大小(1)

class A { static int a; };
int main(){
cout<<sizeof(A)<<endl;// 输出 1;
A a;
cout<<sizeof(a)<<endl;// 输出 1;
return 0;
}

静态成员存放在静态存储区,不占用类的大小, 普通函数也不占用类大小

class A { static int a; int b; };;
int main(){
cout<<sizeof(A)<<endl;// 输出 4;
A a;
cout<<sizeof(a)<<endl;// 输出 4;
return 0;
}

静态成员a不占用类的大小,所以类的大小就是b变量的大小 即4个字节

五、为什么一般构造函数定义为虚函数?析构函数不定义为虚函数?

为什么析构函数一般写为虚函数?

如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全,造成内存泄漏。

所以在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生,要将基类的析构函数声明为虚函数。

为什么构造函数不写为虚函数?

从存储空间角度:虚函数对应一个vtable,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。

从使用角度:虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

六、static的作用(作用域限制)

static

​有时候希望某些全局变量或者函数只在本文件中被使用,而不能被其他外部文件引用,这个时候可以在全局变量前加一个static说明,这样不同的人编写不同的变量或者函数时不用担心重名的问题,即使重名了也互不干扰

默认初始化为0,包括未初始化的全局静态变量与局部静态变量,都存在全局未初始化区

静态变量在函数内定义,始终存在,且只进行一次初始化,具有记忆性,其作用范围与局部变量相同,函数退出后仍然存在,但不能使用​

静态局部变量:

  1. 静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在整个程序运行期间始终存在。
  2. 静态局部变量只初始化一次,并且之后再次调用函数时不再重新分配空间和赋初值,而保留上次函数调用结束时的值(而普通局部变量每调用一次就会重新分配空间并赋一次初值)
  3. 静态局部变量默认初始化为0
  4. 函数调用结束之后静态局部变量依然存在,但是只能在该函数内进行使用该静态局部变量,

extern的作用(作用域扩展)

  1. 将全局变量的作用域扩展到其定义之前:如果全局变量不在文件的开头定义,其作用范围只限定于从定义处到文件结尾,如果在定义点之前的函数想引用该变量,就应该在引用之前使用extern关键字对该变量进行声明,之后该全局变量的作用域就从声明处一直到文件结尾了
  2. 将某一个源文件中全局变量的作用域扩展到其他源文件中:一个C++项目很多情况是由多个源文件构成,如果在一个文件中想引用另一个文件中已定义的全局变量,比如现在两个文件都要使用到同一个全局变量int a,正确的做法应该是:在一个文件中定义变量a,而在另一个文件中使用extern int a;对该变量进行声明,这样就可以两个文件同时使用同一个变量了

const

七、C++ sort()函数实现

sort()源码中采用的是一种叫做IntroSort内省式排序的混合式排序算法,

1.首先进行判断排序的元素个数是否大于stl_threshold,stl_threshold是一个常量值是16,意思就是说我传入的元素规模小于我们的16的时候直接采用插入排序。(为什么用插入排序?因为插入排序在面对“几近排序”的序列时,表现更好,而快排是通过递归实现的,会为了极小的子序列产生很多的递归调用在区间长度小的时候经常不如插入排序效率高)

2.如果说我们的元素规模大于16,那就需要去判断如果是不是能采用快速排序,怎么判断呢?快排是使用递归来实现的,如果说我们进行判断我们的递归深度有没有到达递归深度的限制阈值2*lg(n),如果递归深度没达到阈值就使用快速排序来进行排序

3.如果说大于我们的最深递归深度阈值的话,这个时候说明快排复杂度退化了(比如很不巧基准元素多次选取到了当前区间中最小或最大的元素。这种情况下,每次划分只能将区间缩小1个元素,造成递归深度过深),就会采用我们的堆排序,堆排序是可以保证稳定O(nlogn)的时间复杂度的。

来源:小林coding内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯