360 C++ 面试真题

1、虚函数表的机制

  1. 虚函数的声明和定义:在基类中声明一个函数为虚函数,然后在派生类中进行重写(override)。

class Base {
public:virtual void virtualFunction() {// 虚函数的定义}
};
​
class Derived : public Base {
public:void virtualFunction() override {// 派生类中对虚函数的重写}
};
  1. 虚函数表的创建:对于每个含有虚函数的类,编译器在该类的对象中添加一个指向虚函数表的指针(vptr)。这个指针指向一个虚函数表,其中包含该类的虚函数的地址。

  2. 对象的布局:每个对象都包含一个指向虚函数表的指针。当对象被创建时,这个指针被初始化为指向该类的虚函数表。

Base* obj = new Derived();

obj 是基类指针,指向了一个派生类的对象。实际上,obj 中存储了一个指向虚函数表的指针,而这个虚函数表是派生类的虚函数表。

  1. 动态联编(Dynamic Binding):当调用一个虚函数时,编译器会使用虚函数表来查找正确的函数地址。这样,即使通过基类指针调用虚函数,实际执行的是派生类中的重写函数。

obj->virtualFunction();  // 实际调用的是 Derived 类中的 virtualFunction
  1. 多态性的实现:虚函数表的机制使得程序能够根据对象的实际类型来调用相应的虚函数,实现了多态性。这是C++中实现运行时多态的关键。

2、构造函数可以是虚函数嘛说出原因

  1. 对象的创建时机:虚函数的调用是通过对象的虚函数表(vtable)来实现的,而虚函数表的构建是在对象构造阶段之后完成的。在对象构造期间,对象的虚函数表还没有构建完成,因此在构造函数中无法通过虚函数表调用虚函数。

  2. 虚函数表的指针初始化:对象的虚函数表指针(vptr)是在对象构造期间初始化的,而构造函数是在对象的构造过程中调用的。由于在构造函数调用之前无法确定对象的动态类型,因此在构造函数中调用虚函数会导致无法正确地找到虚函数的实现。

尝试在构造函数中使用虚函数可能会导致不可预测的行为,因为此时对象的状态尚未完全初始化,虚函数表可能尚未准备好。因此,C++语言规定构造函数不能声明为虚函数。

3、C++11新特性

  1. 自动类型推导(Auto):允许编译器推导变量的类型,使代码更加简洁。

auto x = 5; // x的类型将被推导为int
  1. 范围-based for 循环:简化了对容器元素的遍历。

std::vector<int> numbers = {1, 2, 3, 4, 5};
for (const auto& num : numbers) {// 使用num
}
  1. 智能指针:引入了std::shared_ptr和std::unique_ptr等智能指针,用于管理动态分配的内存,帮助防止内存泄漏。

std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
  1. Lambda 表达式:允许在函数内部定义匿名函数,提高代码可读性和灵活性。

auto add = [](int a, int b) { return a + b; };
  1. nullptr:引入了空指针常量nullptr,用于替代传统的空指针NULL。

int* ptr = nullptr;
  1. 强制类型转换(Type Casting):引入了static_cast、dynamic_cast、const_cast、reinterpret_cast等更安全和灵活的类型转换操作符。

double x = 3.14;
int y = static_cast<int>(x);
  1. 右值引用和移动语义:支持通过右值引用实现移动语义,提高了对临时对象的处理效率。

std::vector<int> getVector() {// 返回一个临时vectorreturn std::vector<int>{1, 2, 3};
}std::vector<int> numbers = getVector(); // 使用移动语义
  1. 新的容器和算法:引入了新的容器,如std::unordered_map、std::unordered_set,以及一些新的算法。

std::unordered_map<int, std::string> myMap = {{1, "one"}, {2, "two"}};
  1. 线程支持(std::thread):提供了原生的多线程支持,使得并发编程更加方便。

#include <thread>void myFunction() {// 线程执行的代码
}int main() {std::thread t(myFunction);t.join(); // 等待线程结束return 0;
}

3、介绍4种智能指针

  1. std::unique_ptr

    • std::unique_ptr 是一种独占所有权的智能指针,即同一时刻只能有一个 std::unique_ptr 指向一个对象。当 std::unique_ptr 被销毁时,它拥有的对象会被自动释放。

    • 使用 std::move 可以将所有权从一个 std::unique_ptr 转移到另一个,但源 std::unique_ptr 将变为 null。

    • 适用于在动态分配内存时,需要确保只有一个指针拥有所有权的情况。

#include <memory>int main() {std::unique_ptr<int> ptr = std::make_unique<int>(42);// 使用 std::move 转移所有权std::unique_ptr<int> ptr2 = std::move(ptr);// 此时 ptr 为 nullreturn 0;
}
  1. std::shared_ptr

    • std::shared_ptr 允许多个智能指针共享同一个对象,通过引用计数来追踪对象的引用次数。当最后一个 std::shared_ptr 指向对象的引用计数变为零时,对象会被销毁。

    • 可以通过 std::make_shared 来创建 std::shared_ptr。

#include <memory>int main() {std::shared_ptr<int> ptr1 = std::make_shared<int>(42);// 共享同一个对象std::shared_ptr<int> ptr2 = ptr1;// 引用计数为 2return 0;
}
  1. std::weak_ptr

    • std::weak_ptr 也是用于共享对象所有权的智能指针,但不增加引用计数。它通常用于打破 std::shared_ptr 的循环引用,避免内存泄漏。

    • 可以通过 std::shared_ptr 创建 std::weak_ptr。

#include <memory>int main() {std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);std::weak_ptr<int> weakPtr = sharedPtr;// ...// 当需要使用时,通过 lock 转换为 shared_ptrif (std::shared_ptr<int> lockedPtr = weakPtr.lock()) {// 使用 lockedPtr}return 0;
}
  1. std::auto_ptr(已被废弃)

    • std::auto_ptr 是C++98时代的智能指针,用于实现独占所有权。然而,由于其潜在的不安全性和不明确的所有权传递语义,C++11 引入的 std::unique_ptr 已经取代了 std::auto_ptr。

    • 不建议在现代C++中使用 std::auto_ptr,而应该使用 std::unique_ptr。

4、weak_ptr如何访问指向的数据

std::weak_ptr 通过 lock 方法来访问其指向的数据。lock 方法返回一个 std::shared_ptr 对象,该对象与 std::weak_ptr 共享同一对象,如果 std::weak_ptr 不再有效(底层对象已经被销毁),则返回一个空的 std::shared_ptr。

给个例子:

#include <iostream>
#include <memory>int main() {// 创建 shared_ptr 和 weak_ptr 共享同一个对象std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);std::weak_ptr<int> weakPtr = sharedPtr;// 使用 lock 方法获取 shared_ptrif (std::shared_ptr<int> lockedPtr = weakPtr.lock()) {// lockedPtr 指向的对象仍然存在std::cout << "Value: " << *lockedPtr << std::endl;} else {// weak_ptr 不再有效,对象已经被销毁std::cout << "The object is no longer available." << std::endl;}// sharedPtr 离开作用域,底层对象引用计数变为零,对象被销毁// 再次使用 lock 方法获取 shared_ptrif (std::shared_ptr<int> lockedPtr = weakPtr.lock()) {// 这部分代码不会执行,因为对象已经被销毁std::cout << "Value: " << *lockedPtr << std::endl;} else {std::cout << "The object is no longer available." << std::endl;}return 0;
}

5、右值引用

用于支持移动语义和完美转发。右值引用的语法使用 && 符号。

  1. 移动语义:右值引用允许将资源(如堆上分配的内存)的所有权从一个对象转移到另一个对象,而不是进行深拷贝。这可以提高性能,特别是对于大型数据结构。

  2. 完美转发:右值引用可以在函数模板中实现完美转发,将参数按原样传递给其他函数,包括它们的引用类型。这样,被调用函数能够保留原始参数的左值或右值性质。

给个例子:

#include <iostream>// 移动构造函数演示移动语义
class MyString {
public:MyString() : data(nullptr), size(0) {}// 移动构造函数MyString(MyString&& other) noexcept : data(other.data), size(other.size) {// 将原对象置为空,防止原对象析构时释放内存other.data = nullptr;other.size = 0;}// 输出字符串内容void print() const {std::cout << "Data: " << (data ? data : "null") << ", Size: " << size << std::endl;}private:char* data;size_t size;
};// 函数模板演示完美转发
template <typename T>
void forwardFunction(T&& arg) {std::cout << "Received: ";arg.print();
}int main() {// 使用移动构造函数演示移动语义MyString source;source.print();MyString destination = std::move(source);std::cout << "After move:\n";source.print();  // source 现在为空destination.print();// 使用函数模板演示完美转发MyString myString;myString.print();forwardFunction(myString);                // 传递左值forwardFunction(MyString());              // 传递右值forwardFunction(std::move(myString));     // 传递右值引用return 0;
}

7、将亡值有哪些

  1. 纯右值:纯右值是指不能被命名的、临时的右值表达式。它们通常是在表达式求值过程中创建的,例如函数返回值、类型转换的结果等。纯右值在使用后通常会被立即销毁。

int getValue() {return 42;  // 函数返回的是纯右值
}int main() {int result = getValue();  // getValue() 的结果是纯右值return 0;
}
  1. 将亡引用:将亡引用是C++11引入的一项特性,使用 && 来声明。将亡引用主要用于实现移动语义和完美转发。将亡引用可以绑定到纯右值,同时它也允许我们修改右值。

int main() {int x = 42;int&& rvalueRef = std::move(x);  // 将亡引用绑定到纯右值rvalueRef++;  // 修改右值return 0;
}

8、new出来的数据存放在哪里

在C++中,使用 new 运算符动态分配内存来创建对象时,这些对象的存储位置通常位于堆(Heap)内存中。堆是程序运行时分配的内存区域,它的大小动态增长和缩小,用于存储程序运行时动态分配的对象和数据。

具体来说,使用 new 运算符创建的对象在堆内存中分配一块连续的内存空间,而返回的是该内存块的地址,即指向对象的指针。这个指针可以存储在栈上、其他堆上,或者全局数据区等地方,取决于它的生命周期和作用域。

示例:

#include <iostream>int main() {// 使用 new 运算符在堆上动态分配一个整数int* dynamicInt = new int(42);// 使用动态分配的整数std::cout << "Dynamic Integer: " << *dynamicInt << std::endl;// 记得在不再需要时释放堆上的内存delete dynamicInt;return 0;
}

9、多线程的模式

  1. Fork-Join模式

Fork-Join模式是一种并行计算的模式,主要用于将一个大任务拆分为多个小任务,每个任务独立执行,最后将结果合并。这通常包括一个"分阶段"的过程,其中任务分为更小的子任务,然后在合并阶段将结果组合起来。在C++中,可以使用std::async和std::future等来实现Fork-Join模式。

  1. Producer-Consumer模式

Producer-Consumer模式涉及两类线程:生产者(Producer)线程和消费者(Consumer)线程。生产者线程生成数据,并将其放入共享的缓冲区,而消费者线程从缓冲区中取出数据进行处理。这种模式需要考虑线程之间的同步,以避免竞态条件。

  1. Readers-Writers模式

Readers-Writers模式用于管理对共享资源的读写访问。多个读取者可以同时访问共享资源,但写入者必须独占访问。这有助于提高读取性能,同时确保写入的一致性。在C++中,可以使用互斥锁和条件变量来实现Readers-Writers模式。

  1. Worker Thread模式

Worker Thread模式将任务提交到工作队列,然后由多个工作线程并发处理这些任务。这种模式常用于处理异步事件或需要并行处理的任务。在C++中,可以使用线程池来实现Worker Thread模式。

  1. Pipeline模式

Pipeline模式将一个大任务划分为一系列的阶段,每个阶段由一个线程负责。每个阶段的输出作为下一个阶段的输入。这种模式适用于需要顺序处理的任务,每个阶段可以并行执行。在C++中,可以使用多个线程和队列来实现Pipeline模式。

  1. Actor模型

Actor模型是一种并发计算模型,其中系统由独立的Actor组成,每个Actor都有自己的状态、行为和邮箱。Actors之间通过消息进行通信,而不是共享内存。这种模型可以减少共享状态的复杂性,从而降低并发编程的难度。

10、介绍项目中用到的生产者消费者模式

这里就讲讲生产者消费者模式吧。

生产者-消费者模式是一种并发设计模式,用于解决在多线程环境中生产者和消费者之间的数据共享和同步问题。在这个模式中,有一个生产者线程负责生成数据并将其放入共享的缓冲区,同时有一个或多个消费者线程负责从缓冲区中取出数据并进行处理。

以下是生产者-消费者模式的基本结构和关键概念:

  1. 共享缓冲区:用于存放生产者生成的数据,以便消费者线程可以从中取出数据。这个缓冲区可以是一个队列或者是一个固定大小的缓冲区。

  2. 同步机制:用于确保生产者和消费者之间的正确同步,避免竞态条件和数据不一致的问题。通常使用互斥锁和条件变量来实现。

  3. 信号量或计数器:用于跟踪缓冲区中的可用空间或待处理元素的数量,以便控制生产者和消费者的行为。

给个例子:

#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>const int BUFFER_SIZE = 5;std::queue<int> buffer;
std::mutex bufferMutex;
std::condition_variable bufferNotEmpty;
std::condition_variable bufferNotFull;void producer() {for (int i = 1; i <= 10; ++i) {std::unique_lock<std::mutex> lock(bufferMutex);// 等待缓冲区非满bufferNotFull.wait(lock, [] { return buffer.size() < BUFFER_SIZE; });buffer.push(i);std::cout << "Produced: " << i << std::endl;// 通知消费者缓冲区非空bufferNotEmpty.notify_all();}
}void consumer() {for (int i = 1; i <= 10; ++i) {std::unique_lock<std::mutex> lock(bufferMutex);// 等待缓冲区非空bufferNotEmpty.wait(lock, [] { return !buffer.empty(); });int data = buffer.front();buffer.pop();std::cout << "Consumed: " << data << std::endl;// 通知生产者缓冲区非满bufferNotFull.notify_all();}
}int main() {std::thread producerThread(producer);std::thread consumerThread(consumer);producerThread.join();consumerThread.join();return 0;
}

11、生产者生产太快,消费者消费太慢怎么办(条件变量)

  1. 增大缓冲区大小:增大缓冲区的大小可以容纳更多的数据,减缓缓冲区满的速度。这可以是一个简单的解决方案,但要注意,如果缓冲区过大,可能会占用过多的内存。

  2. 调整生产者和消费者线程的工作速度:这可以通过控制生产者和消费者的休眠时间来实现。例如,如果生产者速度太快,可以在生产者线程中添加短暂的休眠,以减缓其生成数据的速度。

  3. 使用有界队列:可以使用有界队列,即设置一个最大容量,当队列满时,生产者将被阻塞,直到有足够的空间。这样可以防止生产者无限制地生成数据。

12、负载均衡算法有哪些(只答了概念,没具体了解有哪些算法)

  1. 轮询

轮询算法按顺序将请求分配给服务器,每个请求依次分配到下一个服务器。它是最简单的负载均衡算法,适用于各服务器性能相近的场景。

  1. 最小连接数

最小连接数算法将请求分配给当前连接数最少的服务器,以确保连接数相对均衡。这对于处理长连接的应用场景比较有效。

  1. 最短响应时间

最短响应时间算法将请求分配给当前响应时间最短的服务器。通过监测服务器的响应时间,确保将请求发送到最快的服务器上。

  1. IP散列

IP散列算法使用客户端IP地址进行散列计算,然后将请求分配给特定的服务器。这确保相同IP的客户端始终被分配到同一台服务器,适用于需要保持会话一致性的场景。

  1. 加权轮询

加权轮询算法根据服务器的性能设置权重,高性能服务器分配更多的请求。这样可以根据服务器的负载能力分配请求,提高整体性能。

  1. 加权最小连接数

加权最小连接数算法结合了最小连接数和加权轮询的思想,根据服务器的权重和当前连接数分配请求。

  1. Random算法

Random算法随机选择一个服务器来处理请求。虽然简单,但可能导致负载不均衡。

  1. Fair算法

Fair算法根据服务器的响应时间动态调整权重,以确保服务器的负载相对均衡。

  1. Chash算法(一致性哈希)

一致性哈希算法将请求和服务器通过哈希函数映射到一个固定的范围,确保添加或删除服务器时最小化映射的变化,减小影响范围。

13、介绍thrift框架

Apache Thrift(简称Thrift)是一个跨语言的远程服务调用框架,由Apache开发。它被设计用于在不同的系统之间进行通信,并支持多种编程语言。

  1. 多语言支持

Thrift支持多种编程语言,包括但不限于Java、C++、Python、Ruby、C#、PHP等。这使得不同语言的应用程序能够通过Thrift进行通信。

  1. 接口定义语言(IDL)

Thrift使用接口定义语言(IDL)来定义服务接口和数据类型。IDL描述了服务的方法、输入参数、输出参数等信息,同时定义了跨语言的数据结构。

  1. 多协议支持

Thrift支持多种传输协议,包括二进制、JSON、XML等。这使得Thrift可以适应不同的应用场景和网络环境。

  1. 多传输层支持

Thrift支持多种传输层协议,例如TCP、HTTP等。这使得Thrift可以在不同的网络层上工作,从而更灵活地适应各种网络环境。

  1. 服务框架

Thrift提供了生成的服务框架,开发者只需实现IDL中定义的接口,Thrift会生成服务端和客户端的代码。这大大简化了远程服务的开发。

  1. 异步通信

Thrift支持异步通信,可以通过回调方式处理请求,提高系统的并发性能。

  1. 跨语言异常传递

Thrift允许服务端在不同的编程语言中抛出异常,而客户端能够在其自身的异常处理机制中捕获和处理这些异常。

  1. 扩展性

Thrift提供了一些可扩展的机制,例如自定义协议、传输层、数据类型等,以满足特定需求。

14、介绍rpc框架

RPC(Remote Procedure Call,远程过程调用)是一种通信协议,用于使一个程序执行另一个地址空间(通常是共享网络的另一台机器上)的过程调用。RPC框架是实现RPC协议的软件框架,提供了一种简单的方法来进行远程通信,使得在分布式系统中的不同节点上的程序能够像调用本地函数一样调用远程函数。

一些特点:

  1. 远程调用

RPC框架允许程序调用远程机器上的过程,就像调用本地过程一样。调用者无需关心底层的网络通信细节,使得分布式系统的开发更加简便。

  1. 透明性

RPC框架提供了透明性,使得远程调用对于调用者而言是透明的,就像调用本地函数一样。开发者不需要关心底层的网络传输和序列化过程。

  1. 通用性

RPC框架通常是语言无关的,可以支持多种编程语言。这使得在不同语言编写的程序之间进行通信成为可能。

  1. 序列化

RPC框架通过序列化和反序列化来在不同机器之间传递数据。序列化是将数据转换为字节流的过程,反序列化则是将字节流还原为数据的过程。

  1. 服务描述

RPC框架通常使用一种服务描述语言(IDL)来定义服务接口和数据结构。IDL描述了远程服务的方法、输入参数、输出参数等信息。

  1. 通信协议

RPC框架使用不同的通信协议,如HTTP、TCP、UDP等,来实现远程过程调用。

  1. 错误处理

RPC框架提供了错误处理机制,使得远程调用中的错误能够被捕获和处理。

15、介绍项目中的raft协议

16、发生网络故障时raft协议如何工作的

17、tcp和udp的区别

  1. 连接性

TCP是面向连接的协议,它在通信之前需要先建立连接,然后进行数据传输,最后释放连接。UDP是无连接的协议,通信双方之间不需要建立持久的连接。

  1. 可靠性

TCP提供可靠的数据传输,它使用确认、重传、超时和流量控制等机制来确保数据的完整性和顺序性。如果某个数据包丢失或损坏,TCP会尝试重新传输。UDP不提供可靠性,它将尽最大努力交付数据,但不保证顺序或完整性。

  1. 数据流

TCP是面向字节流的,它保证数据以正确的顺序到达接收端。UDP是面向数据包的,每个数据包是独立的,可能以不同的顺序到达接收端。

  1. 开销

TCP的连接建立和维护会引入一定的开销,包括三次握手、数据传输和四次挥手等。UDP没有连接的开销,因此更轻量级。

  1. 适用场景

TCP适用于对可靠性要求较高的应用,如文件传输、Web浏览、电子邮件等。UDP适用于对实时性要求较高、可以容忍一定数据丢失的应用,如音视频传输、实时游戏等。

  1. 流量控制

TCP使用流量控制机制来防止发送方发送速度过快导致接收方无法处理的情况。UDP不提供流量控制,发送方可以按照自己的速率发送数据。

  1. 拥塞控制:

TCP有拥塞控制机制,通过慢启动、拥塞避免等算法来适应网络的拥塞情况。UDP没有拥塞控制,发送方会不断发送数据,无法感知网络的拥塞情况。

18、介绍websocket

WebSocket是一种在单个TCP连接上提供全双工通信的协议,允许在客户端和服务器之间进行实时双向数据传输。它与传统的HTTP通信不同,HTTP是基于请求-响应模型的,每次请求都需要服务器响应,而WebSocket允许在一次握手之后,双方之间建立持久连接,可以随时发送数据。

原理:

  1. 握手过程

WebSocket通信始于一个特殊的握手过程,通过HTTP协议完成。客户端通过发送一个HTTP请求,其中包含了WebSocket的协议信息,服务器在接收到请求后协商是否升级到WebSocket。如果协商成功,连接将升级到WebSocket。

  1. 全双工通信

一旦握手成功,WebSocket连接就变成了全双工通信,允许客户端和服务器双向实时通信。这与传统的HTTP通信模型不同,无需等待请求-响应的周期。

  1. 持久连接

WebSocket连接是持久的,不像HTTP那样每次请求都需要重新建立连接。这减少了每次通信的开销,提高了实时性。

  1. 帧格式

WebSocket使用帧(Frame)来传输数据,帧包括了一些控制帧和数据帧,以及一些用于处理长度、掩码等信息的标志位。这种帧格式可以更有效地传输小块数据。

  1. 协议标识符

WebSocket的协议标识符是"ws"(WebSocket)或"wss"(WebSocket Secure)。"ws"表示普通的WebSocket连接,"wss"表示WebSocket连接上加了TLS/SSL加密层的安全连接。

  1. 安全性

为了提高安全性,可以在WebSocket上加入TLS/SSL层,形成WebSocket Secure(WSS)。这样可以加密通信内容,防止中间人攻击等安全威胁。

  1. 适用场景

WebSocket适用于需要实时性、低延迟的应用场景,如在线聊天、实时通知、在线协作编辑等。它在传输时的开销相对较小,适合频繁的小数据传输。

19、介绍redis,为什么redis快?

  1. 数据存储在内存中

Redis将所有数据存储在内存中,通过在内存中读写数据,避免了传统磁盘数据库频繁的I/O操作。内存的读写速度远远快于磁盘,因此Redis能够提供非常高的读写性能。

  1. 单线程模型

Redis采用单线程的事件循环模型,这使得Redis能够充分利用CPU,避免了多线程的切换开销。虽然Redis是单线程的,但它通过非阻塞的方式处理多个连接,实现了并发性。

  1. 非阻塞I/O

Redis使用了非阻塞的I/O模型,通过事件轮询机制处理多个客户端请求。这种方式可以在不阻塞其他请求的情况下处理某个请求,提高了系统的并发性能。

  1. 优秀的数据结构支持

Redis支持丰富的数据结构,如字符串、哈希表、列表、集合、有序集合等。这些数据结构的实现经过优化,能够在内存中高效存储和访问数据。

  1. 持久化机制

Redis提供了多种持久化机制,包括RDB快照和AOF日志。虽然Redis将数据存储在内存中,但通过定期将内存中的数据快照到磁盘,可以保障数据的持久性。此外,AOF日志可以记录每次写操作,用于在重启后恢复数据。

  1. 基于事件的通知

Redis支持基于事件的通知机制,客户端可以订阅对某个键进行的操作。这种机制可以用于构建实时性较高的应用,如实时通知系统。

  1. 简单而强大的原子操作:

Redis提供了一系列的原子操作,这些操作可以在一个命令中完成,保证了在多个操作之间的原子性。这对于构建高效的分布式系统非常有帮助。

20、redis的两种落盘方式?

  1. RDB(Redis DataBase)快照持久化

    • RDB是一种周期性将内存中的数据保存到磁盘的方式。它通过在指定的时间间隔内生成数据的快照(快照文件以.rdb为后缀),并将该快照文件保存到磁盘上。这个过程是一个异步操作,Redis会单独fork一个子进程来执行快照的生成工作,父进程继续处理客户端请求。

    • RDB持久化适合用于数据备份、灾难恢复等场景。你可以配置Redis定期生成快照,也可以手动执行SAVE或BGSAVE命令来生成快照。然而,RDB持久化的缺点是,如果系统突然宕机,可能会丢失最后一次快照之后的数据。

  2. AOF(Append-Only File)日志文件持久化

    • AOF持久化是通过将每个写操作追加到一个文件中,以保证数据的持久性。AOF文件是一个只追加、不截断的日志文件。通过记录每个写操作,AOF文件可以在服务器重启时通过重新执行这些写操作来还原数据。

    • AOF持久化适合用于数据的实时恢复和灾难恢复等场景。相比RDB,AOF的缺点是持久化文件通常较大,且重启时恢复速度相对较慢。

21、redis如何做分布式?(不会)

  1. Redis Cluster

  • Redis Cluster是Redis官方提供的分布式解决方案,支持水平分片,将数据分散存储在多个节点上。每个节点只存储部分数据,通过哈希槽(hash slot)将数据划分到不同的节点上。Redis Cluster提供了高可用性和容错性,支持节点的动态扩缩,即可以动态添加或删除节点。

  • Redis Cluster的一些特性:

    • 自动分片:Redis Cluster通过哈希槽自动分片,将数据均匀地分布在多个节点上。

    • 节点间通信:节点之间通过Gossip协议进行通信,用于发现其他节点和维护集群的状态。

    • 主从复制:每个分片有一个主节点和多个从节点,主节点负责读写,从节点负责复制主节点的数据,提供读取和故障转移支持。

    • 故障转移:当主节点发生故障时,Redis Cluster能够通过选举新的主节点来实现故障转移。

    • 客户端分区:客户端可以直接连接到任意一个节点,不需要中间代理。客户端根据哈希槽决定数据应该存储在哪个节点上。

  1. Redis Sentinel

  • Redis Sentinel是用于高可用性(High Availability)的监控系统,可以监控多个Redis实例,发现并通知客户端有关故障转移的信息。虽然Sentinel本身不提供数据分片,但可以与多个独立的Redis实例结合使用,每个实例负责存储一部分数据,从而实现分布式的数据存储。

  • Redis Sentinel的一些特性:

    • 监控:Sentinel负责监控Redis节点的健康状态,及时发现主节点故障。

    • 故障转移:Sentinel可以在主节点故障时自动进行故障转移,选择一个从节点升级为主节点。

    • 配置管理:Sentinel可以根据配置文件中的规则,自动进行节点的添加、删除、故障检测等管理操作。

    • 通知:Sentinel通过发布订阅模式向客户端发送有关节点状态变化的通知。

22、决策树节点分裂的算法?

  1. ID3(Iterative Dichotomiser 3)

  • ID3是早期的决策树算法,它基于信息论中的信息增益来选择最优的特征进行分裂。信息增益的计算使用熵(Entropy)的概念,熵度量了数据的不确定性。在每个节点上,算法计算每个特征的信息增益,选择具有最大信息增益的特征进行分裂。

  • 步骤:

    1. 计算当前节点的熵。

    2. 对于每个特征,计算使用该特征分裂后的加权平均熵。

    3. 计算信息增益,选择信息增益最大的特征进行分裂。

  • ID3的主要问题之一是对于具有大量取值的特征,倾向于选择该特征进行分裂,导致生成的树容易过拟合。

  1. C4.5

  • C4.5是ID3的改进版本,它使用信息增益比(Gain Ratio)来选择最优的特征进行分裂。信息增益比对信息增益进行了归一化,解决了ID3中对具有大量取值特征的偏好问题。

  • 步骤:

    1. 计算当前节点的熵。

    2. 对于每个特征,计算使用该特征分裂后的加权平均熵。

    3. 计算信息增益比,选择信息增益比最大的特征进行分裂。

  • C4.5还引入了剪枝机制,以防止过拟合。它通过递归地将树剪枝,选择在剪枝后损失最小的子树。

23、会Python嘛,是否用过pandas之类的库,平时如何学Python 的

24、leetcode23.合并k个升序链表(用优先队列秒了)

思路:

  1. 分治合并:将链表数组划分为两部分,递归地合并左半部分和右半部分。

  2. 两两合并:设计一个函数用于合并两个升序链表,具体的合并方式可以参考合并两个有序链表的方法。

  3. 递归结束条件:当链表数组为空时,返回空链表;当链表数组中只有一个链表时,返回该链表。

参考代码:

#include <vector>using namespace std;// 链表节点的定义
struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(nullptr) {}
};class Solution {
public:// 合并两个升序链表的函数ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {if (!l1) return l2;if (!l2) return l1;if (l1->val < l2->val) {l1->next = mergeTwoLists(l1->next, l2);return l1;} else {l2->next = mergeTwoLists(l1, l2->next);return l2;}}// 分治合并链表数组的函数ListNode* mergeKLists(vector<ListNode*>& lists) {if (lists.empty()) return nullptr;int n = lists.size();// 分治合并while (n > 1) {for (int i = 0; i < n / 2; ++i) {lists[i] = mergeTwoLists(lists[i], lists[n - 1 - i]);}n = (n + 1) / 2;}return lists[0];}
};// 用于创建链表的辅助函数
ListNode* createList(vector<int>& nums) {ListNode dummy(0);ListNode* current = &dummy;for (int num : nums) {current->next = new ListNode(num);current = current->next;}return dummy.next;
}// 用于输出链表的辅助函数
void printList(ListNode* head) {while (head) {cout << head->val << " ";head = head->next;}cout << endl;
}int main() {Solution solution;// 示例输入vector<vector<int>> input = {{1,4,5},{1,3,4},{2,6}};// 将示例输入转换为链表数组vector<ListNode*> lists;for (auto& nums : input) {lists.push_back(createList(nums));}// 调用合并链表数组的函数ListNode* result = solution.mergeKLists(lists);// 输出合并后的链表printList(result);return 0;
}

25、反问,面试官说主要用c++和python的,同时给了建议可以学一学c++服务端开发方向的内容。

还有一些服务端开发相关的问题记不清了,但其它的基本都答出来了,应该是过了,总体来说360的面试强度还是比较大的。

以上便是这位小伙伴的两面面经,就如其本人来说,面试强度还是蛮大的,所以大家也要加油呀~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/635003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用antd design pro 及后端nodejs express 结合minio进行文件的上传和下载管理

使用Ant Design Pro前端框架结合Node.js Express后端服务以及MinIO作为对象存储&#xff0c;实现文件上传和下载管理的基本步骤如下&#xff1a; 1. 安装所需依赖 在Node.js Express项目中安装minio客户端库&#xff1a; npm install minio --save 在前端项目&#xff08;假…

c语言字符串分割函数strtok_s和strtok

strtok_s和strtok是C语言提供的字符串分割函数&#xff0c;用于将一个字符串按照指定的分隔符进行分割成多个子字符串。 strtok_s是C11标准库中提供的安全版本的字符串分割函数&#xff0c;其基本语法如下&#xff1a; char* strtok_s(char* str, const char* delim, char** …

航空飞行器运维VR模拟互动教学更直观有趣

传统的二手车鉴定评估培训模式存在实践性不强、教学样本不足、与实际脱节等一些固有的不足。有了VR虚拟仿真技术的加持&#xff0c;二手车鉴定评估VR虚拟仿真实训系统逐渐进入实训领域&#xff0c;为院校及企业二手车检测培训提供了全新的解决方案。 高职院校汽车专业虚拟仿真实…

DC-3靶机刷题记录

靶机下载地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1-P5ezyt5hUbmmGMP4EI7kw?pwdrt2c 提取码&#xff1a;rt2c 参考&#xff1a; http://t.csdnimg.cn/hhPi8https://www.vulnhub.com/entry/dc-32,312/ 官网http://t.csdnimg.cn/5mVZ7DC-3 (1).pdfhttps://…

模具制造企业ERP系统有哪些?企业怎么选型适配的软件

模具的生产管理过程比较繁琐&#xff0c;涵盖接单报价、车间排期、班组负荷评估、库存盘点、材料采购、供应商选择、工艺流转、品质检验等诸多环节。 有些采用传统管理手段的模具制造企业存在各业务数据传递不畅、信息滞后、不能及时掌握订单和车间生产情况&#xff0c;难以对…

【CF比赛记录】 —— Codeforces Round 920 (Div. 3)(A、B、C、D)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;CF比赛记录 &#x1f48c;其他专栏&#xff1a; &#x1f534;每日一题 &#x1f7e1; cf闯关练习 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓…

学习使用微信小程序实现智能名片电子名片功能代码

学习使用微信小程序实现智能名片电子名片功能代码 拨打手机号功能一键复制信息功能定位导航功能存入手机通讯录功能转发分享功能 拨打手机号功能 wx.makePhoneCall({phoneNumber: qipa250 //仅为示例&#xff0c;并非真实的电话号码 })一键复制信息功能 wx.getClipboardData(…

【ARMv8M Cortex-M33 系列 7.1 -- xPSR | CFSR | HFSR | BFAR | MMFAR 寄存器】

文章目录 问题背景Cortex-M33 Fault 寄存器介绍xPSR (程序状态寄存器)CFSR (可配置故障状态寄存器)HFSR (硬件故障状态寄存器)BFAR (总线故障地址寄存器)MMFAR (内存管理故障地址寄存器) 问题背景 由于在RA4M2&#xff08;Cortex-M33&#xff09;移植RT-Thread OS的时候遇到了…

mysql limit

语法 SELECT * FROM TABLE_NAME LIMIT 起始位置&#xff0c;偏移量注&#xff1a; 起始位置从0开始 示例 查询的第1条数据到第100条数据 limit 0,100查询的第101条数据到第200条数据 limit 100,100注意不要用 limit 101,100示例2 limit 语句应放在order by语句后面执行 …

Java调用WebService接口,SOAP协议HTTP请求返回XML对象

Java调用Web service接口SOAP协议HTTP请求&#xff0c;解析返回的XML字符串&#xff1a; 1. 使用Java的HTTP库发送SOAP请求&#xff0c;并接收返回的响应。 可以使用Java的HttpURLConnection、Apache HttpClient等库。 2. 将返回的响应转换为字符串。 3. 解析XML字符串&…

第十五届蓝桥杯单片机组——串口通信UART

文章目录 一、什么是串口通信二、UART重要参数三、利用STC-ISP生成初始化代码四、使用UART发送和接收数据 一、什么是串口通信 微控制器与外部设备的数据通信&#xff0c;根据连线结构和传送方式的不同&#xff0c;可以分为两种:并行通信和串行通信。   并行通信:指数据的各位…

亚马逊云科技 WAF 部署小指南(六)追踪 Amazon WAF Request ID,排查误杀原因

众所周知&#xff0c;中国是全球制造业的巨大力量&#xff0c;许多中国企业通过 2B 电商平台网站进行商品销售和采购。在这些电商平台上&#xff0c;Web 应用防火墙&#xff08;WAF&#xff09;成为不可或缺的安全工具。然而&#xff0c;WAF 也可能导致误杀问题。一旦误杀发生&…

计算机毕设thinkphp+mysql+_vue房屋租赁系统h3sem

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp5 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 房屋租赁管理系统有不同的用户角色。不同的用户权限对应不…

【办公类-21-03】20240119 提取不连续的男女学号 set()和list法

背景需求&#xff1a;了解班级幼儿性别比例 查看点名册&#xff0c;发现中4班最初的学号是按照先男后女的方式排列&#xff0c;但是随着幼儿转出&#xff0c;空出一些学号&#xff0c;于是新插班的孩子就插入空的学号&#xff0c;空格插完了&#xff0c;就排在学号尾部。 我想…

Docker Consul详解与部署示例

目录 Consul构成 Docker Consul 概述 Raft算法 服务注册与发现 健康检查 Key/Value存储 多数据中心 部署模式 consul-template守护进程 registrator容器 consul服务部署&#xff08;192.168.41.31&#xff09; 环境准备 搭建Consul服务 查看集群信息 registrato…

YOLOv5改进 | 主干篇 | 华为GhostnetV1一种移动端的专用特征提取网络

一、本文介绍 本文给大家带来的改进机制是华为移动端模型Ghostnetv1,华为GhostnetV1一种移动端的专用特征提取网络,旨在在计算资源有限的嵌入式设备上实现高性能的图像分类。GhostNet的关键思想在于通过引入Ghost模块,以较低的计算成本增加了特征图的数量,从而提高了模型的…

【通知】我的教学文章《Rust跟我学》已全部上线

大家好&#xff0c;我是get_local_info开源库作者带剑书生&#xff0c;现在我的《Rust跟我学》专栏文章已全部上线&#xff0c;它记录了我在写库时获得的重要Rust经验和技巧&#xff0c;是不同于《Rust语言编程》等简单实践的书籍。为您节省了学习时间&#xff0c;让您可以快速…

深度剖析Spring循环依赖(实战Bug)

目录 前言1. 问题所示2. 原理分析3. 基本知识4. Lazy注解 前言 通过实战更好的回馈问题&#xff0c;意识更加深刻 起因是我出现如下问题之后&#xff0c;才意识到中了Spring的循环依赖了&#xff01; 1. 问题所示 在执行项目的时候&#xff0c;出现如下问题&#xff0c;问题…

2024.1.19 寒假训练记录(2)

昨晚的cf打得非常抽象&#xff0c;成功从蓝掉到青&#xff0c;不过在心理预期范围内&#xff0c;可以接受&#xff0c;之后每一场都会跟着打大号&#xff0c;能力的提升比表面上的分数更加重要 文章目录 CF 1922A Tricky TemplateCF 1922B Forming TrianglesCF 1922C Closest …

面试之Glide如何绑定Activity的生命周期

Glide绑定Activity生命周期 Glide.with() 下面都是它的重载方法&#xff0c;Context&#xff0c;Activity&#xff0c;FragmentActivity, Fragment, android.app.Fragment fragment,View都可以作为他的参数&#xff0c;内容大同小异&#xff0c;都是先getRetriever&#xff0…