真的,不是谁都会离职后回你消息的。。

校招八股文学习网站:https://interviewguide.cn
这是阿秀的第「412」篇原创

大家好,我是阿秀。

几天收到一位学习圈圈友的私信:"一直有前司的同事微信让帮忙解决问题怎么办?"

其实这种就挺难处理的,因为你帮不帮都说得过去,还是看情况分析:

1、如果离职前和前同事相处的还不错,问题也不难,那就顺手帮一下也可以,中国说到底还是个人情社会,再加上大家都是混互联网这个圈子的,抬头不见低头见,说不定哪天就又遇上了,说不定以后跳槽就刚好跳到对方所在的公司了,现在留一分情面可能到时候就能用得上,"做人留一线,日后好相见"说的也是这个理儿。

2、如果离职前就相处的一般,离职前该交接的也交接了,那么提一两句也不是不可以,但如果要耗费大量时间和精力去帮忙还是算了吧,除非对方按照兼职给你结算,看在 money 的份上也不是不可以"帮忙"

3、如果是那种一看就只想白嫖你的,甚至在你明确表示自己没空闲时间情况下还喋喋不休纠缠不休,甚至理所当然的,直接拒绝就好,如果继续蛮横纠缠下去的,甚至可以考虑直接删除好友了

我以前见过有些人离职第二天就直接拉黑全部前同事的,当时还不太理解,现在想想可能就是为了杜绝这种被前同事找上门要求帮忙的情况出现。毕竟,不是谁都会离职后回消息的。。。

作为一名(成熟的)社畜,我们还是要勇于向一些不合理的事情说 No,大家都是成年人了,不用不好意思,大家都有自己的工作要做,都有自己的事情要处理,不要因为不好意思而为难自己,谁都是妈生爹养的,都不是天生低人一等的。

至于阿秀自己在从前司字节跳动离职的一年时间里,只跟一个玩的还不错的前同事聊过几次生活上的事,还是因为朋友圈看到了评论了一句,然后聊起来的,要不然基本不会打扰。至于工作上的事更是从来没有聊到过,大家都会很自觉的避开那些话题,就让有些事尘归尘、土归土吧。


昨天分享了一份百度提前批的二面面经:百度提前批二面面经,很多小伙伴说收获不少,今天再分享一位读者的百度提前批一面凉经。

昨天那位读者顺利 oc 拿到口头offer了,算是面试成功了,今天这位读者则没能突破一面,倒在了二面的大门前

成功的经验分享过很多,但失败的案例分享的却不多,今天就来分享下这位读者朋友的一面凉经(个人觉得失败的案例反而更有参考价值)。

对于这份面经中比较经典的面试问题,阿秀已经录入到去年开发的互联网大厂面试真题记录网站:InterviewGuide 中去了,这个网站是阿秀去年为了方便自己找工作而开发的面试网站目前是开源状态,欢迎试用体验

网址:https://top.interviewguide.cn/

目前支持按照行业、公司、岗位、科目、考察时间等查看面试真题,在面试前一天晚上看一下有很大概率会遇到第二天的原因,截止目前已经有不少小伙伴遇到原题了,具体可以看下链接:2023年7月字节跳动后端研发岗面试考察题目Top10局部性原理还真有用!

1、直接提问我简历的第一个Webserver项目

我做了简介,看得出来面试官兴趣不大,还问我这个项目是不是我们学校的课程设计作业,感觉很多人都做这个项目。。。我只能苦笑一句说不是的

2、讲一下程序的内存分区/内存模型?

内存分区,分别是堆、栈、自由存储区、全局/静态存储区、常量存储区和代码区,如下图所示

:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

:就是那些由 new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收

自由存储区:如果说堆是操作系统维护的一块内存,那么自由存储区就是C++中通过new和delete动态分配和释放对象的抽象概念。需要注意的是,自由存储区和堆比较像,但不等价。

全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量和静态变量又分为初始化的和未初始化的,在C++里面没有这个区分了,它们共同占用同一块内存区,在该区定义的变量若没有初始化,则会被自动初始化,例如int型变量自动初始为0

常量存储区:这是一块比较特殊的存储区,这里面存放的是常量,不允许修改

代码区:存放函数体的二进制代码

3、智能指针 unique_ptr 和 shared_ptr 的区别?

shared_ptr实现原理:采用引用计数器的方法,允许多个智能指针指向同一个对象,每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,每当减少一个智能指针指向对象时,引用计数会减1,当计数为0的时候会自动的释放动态分配的资源。

unique_ptr采用的是独享所有权语义,一个非空的unique_ptr总是拥有它所指向的资源。转移一个unique_ptr将会把所有权全部从源指针转移给目标指针,源指针被置空;所以unique_ptr不支持普通的拷贝和赋值操作,不能用在STL标准容器中;局部变量的返回值除外(因为编译器知道要返回的对象将要被销毁)。

如果你拷贝一个unique_ptr,那么拷贝结束后,这两个unique_ptr都会指向相同的资源,造成在结束时对同一内存指针多次释放而导致程序崩溃。

4、shared指针循环了怎么办?

可以使用一个 std::shared_ptr 转换为 std::weak_ptr 来打破循环引用。

阿秀补充:

1、使用 std::weak_ptr

std::weak_ptr 是一种弱引用,它不会增加对象的引用计数。当 std::shared_ptr 引用的对象被销毁时,相应的 std::weak_ptr 将自动变为无效。

你可以将一个 std::shared_ptr 转换为 std::weak_ptr 来打破循环引用。

#include 
class A;
class B;
class A {
public:
    std::shared_ptr b;
    void setB(std::shared_ptr bptr) {
        b = bptr;
    }
};

class B {
public:
    std::weak_ptr a;
    void setA(std::shared_ptr
 aptr) {
        a = aptr;
    }
};

在这个例子中,A 类持有 Bstd::shared_ptr,而 B 类持有 Astd::weak_ptr

2、使用 std::enable_shared_from_this

这个基类提供了一个方法 shared_from_this(),它允许从 this 指针创建一个 std::shared_ptr。通常与 std::weak_ptr 一起使用,以避免循环引用。

class A : public std::enable_shared_from_this {
public:
    std::weak_ptr
 self;
    void setSelf() {
        self = shared_from_this();
    }
};

class B {
public:
    std::shared_ptr
 a;
    void setA(std::shared_ptr
 aptr) {
        a = aptr;
    }
};

在这个例子中,A 类使用 std::enable_shared_from_this 来避免循环引用。

3、避免不必要的共享指针:

有时候,某些对象不需要被 std::shared_ptr 管理。考虑使用裸指针或 std::unique_ptr 来减少不必要的引用计数。

4、使用 std::shared_ptr 的自定义删除器:

通过自定义删除器,可以在对象销毁时执行一些清理操作,从而间接打破循环引用。

5、虚函数的作用?怎么用?

在C++中,虚函数是通过虚函数表(virtual function table)来实现的。当一个类声明了虚函数时,编译器会为该类生成一个虚函数表。虚函数表是一个指向虚函数的指针数组,每个指针指向一个虚函数的实际地址。

当一个对象被创建时,会在对象的内存布局中添加一个指向虚函数表的指针。这个指针被称为虚函数指针(vptr)。当通过指向基类的指针或引用调用虚函数时,实际调用的是虚函数表中对应的虚函数。

虚函数的调用过程如下:

1、编译器根据对象的静态类型确定要调用的虚函数,并通过虚函数表中的偏移量找到虚函数的地址。

2、使用虚函数指针(vptr)访问虚函数表,并根据偏移量找到虚函数的地址。

3、直接调用虚函数

6、类里面把所有普通函数都设置为虚函数可行吗?

不可以,比如构造函数就不能是虚函数,由于创建一个对象时我们总是要明白指定对象的类型,虽然我们可能通过实验室的基类的指针或引用去訪问它但析构却不一定,我们往往通过基类的指针来销毁对象。这时候假设析构函数不是虚函数,就不能正确识别对象类型从而不能正确调用析构函数,所以不可以。

7、线程和进程区别?

进程 线程 协程
定义 资源分配和拥有的基本单位 程序执行的基本单位 用户态的轻量级线程,线程内部调度的基本单位
切换情况 进程CPU环境(栈、寄存器、页表和文件句柄等)的保存以及新调度的进程CPU环境的设置 保存和设置程序计数器、少量寄存器和栈的内容 先将寄存器上下文和栈保存,等切换回来的时候再进行恢复
切换者 操作系统 操作系统 用户
切换过程 用户态->内核态->用户态 用户态->内核态->用户态 用户态(没有陷入内核)
调用栈 内核栈 内核栈 用户栈
拥有资源 CPU资源、内存资源、文件资源和句柄等 程序计数器、寄存器、栈和状态字 拥有自己的寄存器上下文和栈
并发性 不同进程之间切换实现并发,各自占有CPU实现并行 一个进程内部的多个线程并发执行 同一时间只能执行一个协程,而其他协程处于休眠状态,适合对任务进行分时处理
系统开销 切换虚拟地址空间,切换内核栈和硬件上下文,CPU高速缓存失效、页表切换,开销很大 切换时只需保存和设置少量寄存器内容,因此开销很小 直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快
通信方面 进程间通信需要借助操作系统 线程间可以直接读写进程数据段(如全局变量)来进行通信

8、多个线程竞争怎么解决?

可以使用锁来操作,比如互斥锁或者读写锁等。

阿秀补充

线程竞争(race condition)是一个常见的问题,当多个线程尝试同时访问和修改共享数据时,就可能发生。以下是一些解决线程竞争的常见方法:

1、互斥锁(Mutex):使用互斥锁是防止多个线程同时访问共享资源的常见方法。当一个线程获取锁时,其他线程必须等待直到锁被释放。

#include 
std::mutex mtx;
void safeFunction() {
    std::lock_guard lock(mtx);
    // 临界区代码
}

2、读写锁(Read-Write Lock):如果共享资源的读操作远多于写操作,可以使用读写锁。它允许多个读线程同时访问资源,但写操作是排他的。

#include 
std::shared_mutex rw_mutex;
void readFunction() {
    std::shared_lock read_lock(rw_mutex);
    // 读操作
}
void writeFunction() {
    std::unique_lock write_lock(rw_mutex);
    // 写操作
}

3、原子操作:对于简单的数据类型,可以使用原子类型和原子操作来保证操作的原子性,从而避免竞争条件。

#include 
std::atomic atomic_var(0);
void incrementFunction() {
    atomic_var.fetch_add(1, std::memory_order_relaxed);
}

此外,还有条件变量、信号量(Semaphore)以及避免共享等多种方式,解决线程竞争问题。

9、算法题

力扣接雨水,没A出来,我感觉这是导致我一面凉了的主要原因。。。

你好,我是阿秀,普通学校毕业,校招时拿到字节跳动SP、百度、华为、农业银行等6个互联网中大厂offer,毕业后先于抖音部门担任全栈开发工程师,目前在上海某外企带领团队继续从事全栈开发,负责对印项目。

前年和去年分享的很多校招上岸经验也都是出自阿秀的学习圈中的往届上岸人如21届、22届、23届等,阿秀的学习圈置顶帖的「知识图谱」和「精华区」中有很多计算机大学本科&研究生学习以及校招的内容和问题,都是往届沉淀下来的瑰宝。

多看看这些能够帮你的校招之路走的更稳、更顺、更平坦,比如:

也会在自己组建的阿秀的学习圈中分享一些社招跳槽找工作的经验,都是自己一路走过来的经验。