绿盟校招C++研发工程师一面复盘

绿盟校招C++研发工程师一面复盘

  1. 为什么拷贝构造函数使用引用传递而不是值传递?

    首先明确拷贝构造函数是C++中一种特殊的构造函数,作用是:用一个已存在的同类对象,初始化一个全新的同类对象。

    语法原型:

    c 复制代码
    // 正确写法:const 引用传递
    A(const A& other); 

    在拷贝构造函数中使用值传递会触发无限递归 ,导致程序崩溃,这是最根本、最核心 的原因。如果拷贝构造函数的参数写成值传递 A(A other),程序编译运行阶段一定会触发无线递归,最终因栈溢出崩溃。

    为什么值传递会触发无限递归?

    先记住一个C++铁律

    C++中,所有函数值传递参数 ,在函数被调用时,编译器都会自动为这个参数创建一个临时拷贝副本

    在拷贝的过程中,不是简单的内存复制,而是严格调用对应类型的拷贝构造函数来完成的。

    假设我们把拷贝构造函数写成值传递版本

    cpp 复制代码
    class A {
    public:
        // 错误写法:拷贝构造函数 采用 值传递
        A(A other) {
            // 构造逻辑
        }
    };

    当我们在代码中,尝试用一个已存在的A对象a1,创建新对象a2时:A a2(a1);,会触发如下无限循环的调用过程:

    bash 复制代码
    执行A a2(a1);,需要调用拷贝构造函数 A(A other);
                  |
                  V
    该函数的参数是值传递,编译器必须为参数other创建临时拷贝副本,这个副本的数据源是a1,
                  |
                  V
    要创建other这个A类型的副本,就必须再次调用A类型的拷贝构造函数
                  |
                  V
    新的拷贝构造函数调用,参数还是值传递,编译器又要为新的参数创建临时副本,再次调用拷贝构造函数
    这个过程会无限循环下去,没有任何终止条件
    每一次函数调用都会占用栈内存,最终栈内存耗尽,程序触发栈溢出错误并崩溃

    为什么引用传递可以完美解决这个问题呢?

    这是因为C++的引用本质上是原对象的一个别名,它不是一个独立的对象,也不占用新的内存空间,没有拷贝操作就不会触发额外的拷贝构造函数调用,彻底断了递归地源头。

    为什么要加const?

    多出来的const不是可有可无的,而是语法规范加逻辑严谨性的双重要求。

    如果参数只写A& other(非 const 引用),那么我们无法用「const 修饰的常量对象」 来创建新对象,编译器会直接报错。

    比如:

    c 复制代码
    const A a1;
    A a2(a1); // 编译报错!非const引用 无法绑定到 const对象

    const A& other常量引用 ,它的语法规则是:可以绑定到 普通对象、const 常量对象、临时匿名对象,兼容性拉满,不会有任何编译问题。

    拷贝构造函数的核心语义 是:「复制原对象的内容,创建一个新对象」,这个过程中,原对象的状态绝对不应该被改变

    加上const修饰后,编译器会做「语法强校验」:如果我们在拷贝构造函数内部,不小心写了修改other的代码(比如other.num = 10;),编译器会直接报错,从语法上杜绝了误修改原对象的可能,保证代码的健壮性。

    此外,值传递会存在内存开销数据拷贝开销两个问题。

  2. 进程间通信方式中的共享内存为何比套接字快呢?

    共享内存的核心是让多个进程映射同一块物理内存到各自的虚拟地址空间,通信过程完全绕开内核的中转干预。

    而套接字会触发用户态到内核态的切换。用户态与内核态的切换,需要保存、恢复进程上下文,这是操作系统的核心开销之一,套接字的每一次send()/recv()都是系统调用,都要经历用户态->内核态->用户态的切换。

    共享内存无协议开销,数据是直接写入内存的原始字节流,无需封装任何协议头、无需计算校验和、无需处理拥塞控制,CPU开销极低。

  3. 线上CPU飙升如何排查?

    • 首先确认是哪个进程占用CPU过高,登录服务器利用top命令查看各个进程的资源占用情况

    • 确认CPU利用率很高的进程PID,假设1234为某个进程,则通过top -Hp 1234查看具体的线程

    • 假设得到的线程ID是5678,再将线程ID转化为十六进制,得到十六进制的tid162e,此时利用jstack 1234 | grep 162e -A 100查看具体的栈信息。

      jstack命令用于生成当前时刻的线程快照。线程快照是当前每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因。

    • 根据堆栈信息就可以定位到具体是哪行代码导致CPU飙升,对应修复即可。

  4. 高并发设计,如何做限流呢?说一说你知道的限流算法

    限流是限制到达系统的并发请求数,使系统能够正常的处理部分用户的请求,来保证系统的稳定性。限流的本质是因为后端处理能力有限,需要截掉超过处理能力之外的请求,亦或是为了负载均衡客户端对服务端资源的公平调用,防止一些客户端饿死。

    计数限流:每次请求到来时候对计数器进行计数,如果超过阈值就拒绝。

    固定窗口限流:每次请求来,计数器加一,超过阈值,拒绝访问。窗口时间到了,计数器清零。

    滑动窗口限流:主要解决固定窗口在临界时间时,重置计数器,导致下一次窗口涌入大量请求。为每个请求增加一个到达时间,其实就是把时间作为区间记录当前区间的请求数是否小于阈值。

    漏桶算法:水滴(请求)持续滴入漏桶中,底部定速流出(处理请求)。桶满拒绝。

    令牌桶算法:定速往桶里塞入令牌,请求只有拿到令牌才能通过,之后再被服务器处理。桶满丢令牌。

相关推荐
微露清风7 小时前
系统性学习C++-第十九讲-unordered_map 和 unordered_set 的使用
开发语言·c++·学习
_Lzk666888_7 小时前
洛谷用户2002780求关注
c++·其他
UrbanJazzerati8 小时前
解码数据分布:茎叶图和箱形图初学者指南
面试·数据分析
无限进步_8 小时前
【C语言&数据结构】对称二叉树:镜像世界的递归探索
c语言·开发语言·数据结构·c++·git·算法·visual studio
X***07888 小时前
C语言在嵌入式系统开发中的应用与挑战
c++
小芒果_018 小时前
整理归并排序
c++·算法·排序算法·信息学奥赛
玖剹9 小时前
队列+宽搜(bfs)
数据结构·c++·算法·leetcode·宽度优先
oioihoii9 小时前
构建高并发AI服务网关:C++与gRPC的工程实践
开发语言·c++·人工智能