NEWNEWNEW

1.如何在map中编译删除指定的元素

方法1:通过key

方法2:通过迭代器

 2.基类的析构函数为什么建议是虚函数?

如果基类的析构函数不是虚函数会导致派生类的析构函数不会被调用,派生类中分配的资源都不会得到释放,会造成资源泄露的问题

 3.基于内存对齐规则,如何设计一个类?

类里面的成员变量我会按照从大到小的原则排列,比如有doublie,char,int 类型的成员

class GoodDesign {
    double d; // 8字节
    int    i; // 4字节
    char   c; // 1字节
};
// sizeof(GoodDesign) 很可能是 16 字节

通过将最大的double放在最前面,int和char可以紧凑地排列在后面,大大减少了填充字节。

4. 说一说内联函数

简单的频繁计算,防止栈开销过大

#include 

inline int square(int x) {
    return x * x;
}

// filepath: main_inline.cpp
#include "math_utils_inline.h"

int main() {
    int total = 0;
    for (int i = 1; i <= 1000; ++i) {
        // 编译器在这里可能会将代码展开
        total += square(i); 
    }
    std::cout << "Total: " << total << std::endl;
    return 0;
}

当编译器处理 main_inline.cpp 时,它看到了 square 函数的完整定义。在编译 total += square(i); 这一行时,编译器很可能会将其直接替换为 total += i * i;

5.函数调用的过程 

int add(int a, int b) {
    int sum = a + b;
    return sum;
}

int main() {
    int x = 10;
    int y = 20;
    int result = add(x, y);
    return 0;
}

---->main函数的栈帧

      | ...           |
      |---------------|
      | result (未初始化) |
      | y = 20        |  <-- main的栈帧
      | x = 10        |
      |---------------|
      | ...           |
高地址 ^
      |
低地址 v

---->准备调用 add:参数入栈 

      | ...           |
      |---------------|
      | result (未初始化) |
      | y = 20        |  <-- main的栈帧
      | x = 10        |
      |---------------|
      | 20 (参数y)    |  <-- 为调用add准备
      | 10 (参数a)    |
      |---------------|

---> 执行调用:返回地址入栈

      | ...           |
      |---------------|
      | result (未初始化) |
      | y = 20        |  <-- main的栈帧
      | x = 10        |
      |---------------|
      | 20 (参数y)    |
      | 10 (参数a)    |
      |---------------|
      | 返回地址      |  <-- call指令压入
      |---------------|

--->add 函数内部:建立新栈帧

      | ...           |
      |---------------|
      | ...           |  <-- main的栈帧
      |---------------|
      | 20 (参数y)    |
      | 10 (参数a)    |
      |---------------|
      | 返回地址      |
      |---------------|
      | 旧的EBP/RBP   |
      |---------------| <-- 新的EBP/RBP指向这里 (add的栈帧基址)
      | sum (未初始化)  |
      |---------------| <-- 新的ESP/RSP指向这里 (add的栈顶)

--->add 函数返回:销毁栈帧 

      | ...           |
      |---------------|
      | ...           |
      |---------------|
      | 20 (参数y)    |
      | 10 (参数a)    |
      |---------------|
      | 返回地址      |  <-- ESP/RSP指向这里
      |---------------|

--->执行返回:回到调用者 

--->调用后:清理栈 

6.reactor 和 proactor 区别 

Reactor 的核心是等待I/O就绪,然后由应用线程去执行I/O。它是一种同步事件驱动的模式。

Proactor 的核心是发起异步I/O,然后由操作系统完成I/O后通知应用。它是一种异步事件驱动的模式。

 7.水平触发(LT)和边缘触发(ET)的区别

LT

// 当 epoll_wait 返回一个可读事件的 fd
char buf[1024];
int n = read(fd, buf, sizeof(buf)); // 读一次即可,下次还会通知
if (n > 0) {
    // 处理数据...
}

 应用程序可以不必一次性将缓冲区的数据全部读完。如果这次只读了一部分,没关系,因为缓冲区里还有数据(条件仍然满足),下一次调用 epoll_wait 时,它会再次通知你。

ET

// 当 epoll_wait 返回一个可读事件的 fd
char buf[1024];
while (true) {
    int n = read(fd, buf, sizeof(buf));
    if (n > 0) {
        // 处理数据...
    } else if (n == 0) {
        // 对端关闭连接
        break;
    } else if (n == -1) {
        // 必须检查 EAGAIN 或 EWOULDBLOCK
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // 数据已经全部读完,可以退出循环了
            break; 
        }
        // 其他错误
        perror("read error");
        break;
    }
}

 收到一个事件通知后(比如可读事件),应用程序必须循环地、持续地从该 fd 读取数据,直到读完所有数据,返回 EAGAIN 或 EWOULDBLOCK 错误为止。如果在收到通知后没有把数据读完,那么 epoll_wait 将不会再次为这个 fd 的剩余数据而返回。你将会丢失处理剩余数据的机会,直到下一次有新的数据到达该 fd。

 

你可能感兴趣的:(数据结构)