【C++】常规面试题

1、语法类

  1. 强制类型转换

    1. static_cast:和C语言类似,(type)expression 注意:不执行类型检查,需要自己确保合法性
    2. dynamic_cast:应用于父子类层次结构的类型转换,要使用有效基类至少需要一个虚函数,运行时执行类型检查,转换失败返回空指针(对于指针类型)或者抛出异常(对于引用类型)
    3. const_cast:使用:const_cast <new_type> (expression),new_type 必须是一个指针、引用或者指向对象类型成员的指针

2、Socket通信

客户端主动发起请求,客户端---服务端:一般为多对一

通信:指定IP和端口号

关键点:IP、端口、通信数据

通信数据:两端(发送端和接收端)数据存储顺序要一致,否则接收端不能解析得到发送端一致的是数据,即需要字节序

2.1 字节序

单字节没有字节序问题,数据格式大于1个字节需要考虑字节序问题

1:大端(也称为网络字节序)

低高高低,低位字节存储在高地址

网络通信按照大端通信,且符合阅读顺序

2:小端(也称为主机字节序)

低低高高,低位字节存储低地址

3:大端小端转换

#include <arpa/inet.h>
// u:unsigned
// 16: 16位, 32:32位
// h: host, 主机字节序
// n: net, 网络字节序
// s: short
// l: int

// 这套api主要用于 网络通信过程中 IP 和 端口 的 转换
// 将一个短整形从主机字节序 -> 网络字节序
uint16_t htons(uint16_t hostshort);	
// 将一个整形从主机字节序 -> 网络字节序
uint32_t htonl(uint32_t hostlong);	

// 将一个短整形从网络字节序 -> 主机字节序
uint16_t ntohs(uint16_t netshort)
// 将一个整形从网络字节序 -> 主机字节序
uint32_t ntohl(uint32_t netlong);

大端转小端为例:IPV4和IPV6都适用

step1:由于IP地址一般是使用字符串来表示,需要先将字符串转换成整型数

// 主机字节序的IP地址转换为网络字节序
// 主机字节序的IP地址是字符串, 网络字节序IP地址是整形
int inet_pton(int af, const char *src, void *dst); 

step2:再将大端的整型数转换成小端的IP地址

#include <arpa/inet.h>
// 将大端的整形数, 转换为小端的点分十进制的IP地址        
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

2.2 TCP通信

概念:传输层协议,可靠安全的连接,连接需要三次握手,断开需要四次挥手

先启动服务器进程,再启动客户端进程

套接字创建有两种:监听套接字、通信套接字

  • 监听套接字:只需要有一个
  • 通信套接字:有几个客户端和服务器连接就有几个

1:服务器端通信

①创建套接字,用于监听

int lfd = socket();

②绑定套接字IP端口

bind();

③监听客户端连接,检测有没有客户端(一次最多检测128个连接请求)

只需要调用一次

listen();

④等待并接受客户端请求,返回值为新的fd用于通信

int cfd = accept();

默认阻塞性函数,有客户端发起连接则解除阻塞,一次调用accept只能连接一个客户端

⑤通信,进行读写操作

⑥断开连接

close();

一次close(),进行两次挥手

2:客户端通信

①创建套接字,用于通信

int cfd = socket();

②连接服务器,指定服务器的IP和端口(注:输入参数IP为大端)

connect();

③通信,进行读写操作

④断开连接

close();

3:阻塞问题

上述函数中如 accept()、read()/write(),属于阻塞函数,无法处理多连接,无法实现并发

并发实现:

  • 多线程
  • 多进程
  • 使用IO多路转接(复用)实现
  • 使用IO多路转接 + 多线程实现

(1)采取多线程实现

  • 主线程用于监听
  • 子线程用于通信

父子线程共用同一个地址空间中的文件描述符,因此每当在主线程中建立一个新的连接,都需要将得到文件描述符值保存起来,不能在同一变量上进行覆盖

只要保证存储每个有效文件描述符值的变量对应不同的内存地址,在使用的时候就不会发生数据覆盖的现象,造成通信数据的混乱了

(2)采取多进程实现

僵尸进程:子进程已经结束,但是资源未被回收,仍然占据进程表的位置

2.3 线程池

频繁创建线程和销毁线程需要时间,因此引入线程池

组成:任务队列、工作线程、管理线程

  • 任务队列

将要处理的任务加入队列,已经完成的任务从队列中删除

生产者线程:向任务队列中添加任务的线程

  • 工作线程

消费者线程:读任务队列,取出任务并处理

任务队列为空,工作的线程被阻塞

  • 管理线程

检测任务队列中的任务数量以及处于忙状态的工作线程数

线程池结构体:

// 线程池结构体
struct ThreadPool
{
    // 任务队列
    Task* taskQ;
    int queueCapacity;  // 容量
    int queueSize;      // 当前任务个数
    int queueFront;     // 队头 -> 取数据
    int queueRear;      // 队尾 -> 放数据

    pthread_t managerID;    // 管理者线程ID
    pthread_t *threadIDs;   // 工作的线程ID
    int minNum;             // 最小线程数量
    int maxNum;             // 最大线程数量
    int busyNum;            // 忙的线程的个数
    int liveNum;            // 存活的线程的个数
    int exitNum;            // 要销毁的线程个数
    pthread_mutex_t mutexPool;  // 锁整个的线程池
    pthread_mutex_t mutexBusy;  // 锁busyNum变量
    pthread_cond_t notFull;     // 任务队列是不是满了
    pthread_cond_t notEmpty;    // 任务队列是不是空了

    int shutdown;           // 是不是要销毁线程池, 销毁为1, 不销毁为0
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

alwaysonlinee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值