地平线后端日常实习 地平线 C++之解答

项目2

5.为什么选epoll

主要是epoll相对于select,poll更高效。

epoll为什么高效:

(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把当前进程往设备等待队列中挂一次,而epoll只要一次拷贝,而且把当前进程往等待队列上挂也只挂一次,这也能节省不少的开销。

6.epoll是最快的么,什么场景下

不一定。epoll的一个缺点,当事件触发比较频繁时,回调函数也会被频繁触发,此时效率就未必比select 或 poll高了。所以epoll的最佳使用情景是:连接数量多,但活跃的连接数量少。

7.用的水平还是垂直触发,有什么区别

LT模式(水平触发)下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作;

而在ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。

8.epoll是如何操作fd的

epoll提供了三个函数,epoll_create、epoll_ctl和epoll_wait。 首先创建一个epoll对象,然后使用epoll_ctl对这个对象进行操作(添加、删除、修改),把需要监控的描述符加进去,这些描述符将会以epoll_event结构体的形式组成一颗红黑树,接着阻塞在epoll_wait,进入大循环,当某个fd上有事件发生时,内核将会把其对应的结构体放入一个链表中,返回有事件发生的链表。

C++

9.c++生成可执行文件过程

C++和C语言类似,一个C++程序从源码到执行文件,有四个过程,预编译、编译、汇编、链接。

预编译:这个过程主要的处理操作如下:

(1) 将所有的#define删除,并且展开所有的宏定义

(2) 处理所有的条件预编译指令,如#if、#ifdef

(3) 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。

(4) 过滤所有的注释,如//、/**/

(5) 添加行号和文件名标识。

编译:这个过程主要的处理操作如下:

(1) 词法分析:将源代码的字符序列分割成一系列的记号。

(2) 语法分析:对记号进行语法分析,产生语法树。

(3) 语义分析:判断表达式是否有意义。

(4) 代码优化:

(5) 目标代码生成:生成汇编代码。

(6) 目标代码优化:

汇编:这个过程主要是将汇编代码转变成机器可以执行的指令。

链接:将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。

10.多态有哪些

编译期间如何实现多态:重载

执行期间如何实现多态:多态

11.怎么实现虚函数(虚表、虚指针)

C++实现虚函数的原理是虚函数表+虚表指针。

当一个类里存在虚函数时,编译器会为类创建一个虚函数表,虚函数表是一个数组,数组的元素存放的是类中虚函数的地址。

同时为每个类的对象添加一个隐藏成员,该隐藏成员保存了指向该虚函数表的指针。该隐藏成员占据该对象的内存布局的最前端。

所以虚函数表只有一份,而有多少个对象,就对应多少个虚函数表指针。

12.模板展开在哪个阶段

编译阶段

13.用模板写过哪些功能

14.private、public、protected三种修饰符作用于继承,哪些可见哪些不可见

类中成员访问属性有三种:

(1)私有成员(变量和函数)只限于类成员访问,由private限定;

(2)公有成员(变量和函数)允许类成员和类外的任何访问,由public限定;

(3)受保护成员(变量和函数)允许类成员和派生类成员访问,不允许类外的任何访问。所以protected对外封闭,对派生类开放。

15.裸socket连接流程

(1)服务器根据地址类型( ipv4, ipv6 )、 socket 类型、协议创建 socket。

(2)服务器为 socket 绑定 IP 地址和端口号。

(3)服务器 socket 监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket 并没有被打开 。

(4)客户端创建 socket。

(5)客户端打开 socket,根据服务器 IP 地址和端口号试图连接服务器 socket。

(6)服务器 socket 接收到客户端 socket 请求,被动打开,开始接收客户端请求,直到客户端返回连接信息 。这时候 socket 进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端连接请求 。

(7)客户端连接成功,向服务器发送连接状态信息 。

(8)服务器 accept 方法返回,连接成功 。

(9)客户端向 socket 写入信息 。

(10)服务器读取信息 。

(11)客户端关闭 。

(12)服务器端关闭 。

16.新特性有哪些

C++后续版本更是发展了不少新特性,如C++11中引入了nullptr、auto变量、Lambda匿名函数、右值引用、智能指针。

17.为什么用智能指针

使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等。

正是因为指针存在这样的问题,C++便引入了智能指针来更好的管理堆内存。智能指针是利用了一种叫做RAII(资源获取即初始化)的技术对普通的指针进行封装,这使得智能指针实质是一个对象,行为表现的却像一个指针。

因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,自动释放资源。这样程序员就不用再担心内存泄露的问题了。

C++里面有四个指针:auto_ptr、unique_ptr、shared_ptr、weak_ptr,后面三个是C++11支持的,第一个被C++11弃用。

18.怎么设计share_ptr,引用计数存在哪里

实现原理:有一个引用计数的指针类型变量,专门用于引用计数,使用拷贝构造函数和赋值拷贝构造函数时,引用计数加1,当引用计数为0时,释放资源。

19.全用share_ptr就能解决内存泄漏了么

会出现内存泄露问题。

共享指针的循环引用计数问题:当两个类中相互定义shared_ptr成员变量,同时对象相互赋值时,就会产生循环引用计数问题,最后引用计数无法清零,资源得不到释放。

可以使用weak_ptr,weak_ptr是弱引用,weak_ptr的构造和析构不会引起引用计数的增加或减少。我们可以将其中一个改为weak_ptr指针就可以了。比如我们将class B里shared_ptr换成weak_ptr。

20.weak_ptr的lock()函数怎么知道share_ptr是否存在

lock()方法的功能是:判断weak_ptr所指向的shared_ptr对象是否存在。若存在,则这个lock方法会返回一个指向该对象的shared_ptr指针;若它所指向的这个shared_ptr对象不存在,则lock()函数会返回一个空的shared_ptr。

21.静态变量初始化顺序

对于C语言的全局和静态变量,初始化发生在任何代码执行之前,属于编译期初始化。

而C++标准规定:全局或静态对象当且仅当对象首次用到时才进行构造。

编程

22.写一个线程池

设计思路:

实现线程池有以下几个步骤: (1)设置一个生产者消费者队列,作为临界资源。

(2)初始化n个线程,并让其运行起来,加锁去队列里取任务运行

(3)当任务队列为空时,所有线程阻塞。

(4)当生产者队列来了一个任务后,先对队列加锁,把任务挂到队列上,然后使用条件变量去通知阻塞中的一个线程来处理。

代码略

2023.1.10 (40分钟)

1.malloc种brk和mmp的区别(没看过。。。)

问题有点迷糊,不清楚面试官的点是什么

2.实现一个单例模式

单例实现原理是,将能够创建对象的函数都设置为private,通过静态成员返回一个实例。

有两种方式,一个是懒汉式,一个是饿汉式。懒汉式需要考虑加锁。

实现代码如下:

#include <iostream>
#include <pthread.h>
using namespace std;

class singleInstance{
public:
	static singleInstance* GetsingleInstance(){
		if (instance == NULL){
			pthread_mutex_lock(&mutex);//mlock.lock();
			if (instance == NULL){
				instance = new singleInstance();
			}
			pthread_mutex_unlock(&mutex);//mlock.unlock();
		}
		return instance;
	};
	~singleInstance(){};
	static pthread_mutex_t mutex;//mutex mlock; 加锁互斥
private:// 涉及创建对象的函数都设置为private
	singleInstance(){};
	singleInstance(const singleInstance& other){};
	singleInstance& operator=(const singleInstance& other){ return *this; };
	static singleInstance* instance;
};

//懒汉式,静态变量需要定义
singleInstance* singleInstance::instance = nullptr;
pthread_mutex_t singleInstance::mutex;

int main(){
	// 因为没有办法创建对象,就得采用静态成员函数的方法返回静态成员变量
	singleInstance *s = singleInstance::GetsingleInstance();
	//singleInstance *s1 = new singleInstance(); // 报错
    cout << "Hello World";
    delete s;  // 防止内存泄露
    return 0;
}

下面是饿汉式:

#include <iostream>
#include <pthread.h>
using namespace std;

class singleInstance{
public:
	static singleInstance* GetsingleInstance(){ // 饿汉式,直接创建一个对象,不需要加锁
		static singleInstance instance;
		return &instance;
	};
	~singleInstance(){};
private:// 涉及创建对象的函数都设置为private
	singleInstance(){};
	singleInstance(const singleInstance& other){};
	singleInstance& operator=(const singleInstance& other){ return *this; };
};

int main(){
	// 因为没有办法创建对象,就得采用静态成员函数的方法返回
	singleInstance *s = singleInstance::GetsingleInstance();
	//singleInstance *s1 = new singleInstance(); // 报错
    cout << "Hello World";
    return 0;
}

3.写一个字符串转数字(要求:1.负数;2.十六进制;3.非法字符)

以上答案均来自本人专栏:校招面试考点全解析——C++软件与嵌入式篇(蒋豆芽的秋招打怪之旅)

欢迎大家围观:https://blog.nowcoder.net/jiangwenbo

牛友面经解答 文章被收录于专栏

这个专栏专门用于为牛友解答面经,希望能帮助到大家。

全部评论
实习面这么难吗入职半年了,让我面实习我估计都过不了
2 回复 分享
发布于 2023-01-15 03:37 江苏
楼主过了吗
1 回复 分享
发布于 2023-01-15 11:51 福建
二面应该是想问malloc啥时候用brk或mmap吧,之后可以再延伸一下
点赞 回复 分享
发布于 2023-01-11 21:59 北京
差不多差不多 我年的时候也问了这些 让我写单例模式和生产者消费者模型😂 拿了offer但是后来没去
点赞 回复 分享
发布于 2023-01-17 20:42 江苏
想问下epoll项目是在哪里学的呢
点赞 回复 分享
发布于 2023-01-29 10:42 广东
码住
点赞 回复 分享
发布于 2023-05-29 20:49 广东

相关推荐

12-16 18:18
四川大学 后端
点赞 评论 收藏
分享
11-14 16:09
门头沟学院 Java
Java抽象带篮子:可以看看我的苍穹外卖话术帖子和八股笔记帖子
点赞 评论 收藏
分享
评论
23
148
分享
牛客网
牛客企业服务