《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux... 。

文章目录
- 一、本文面试题目录
-
-
- [71. 如何实现一个简单的内存池?](#71. 如何实现一个简单的内存池?)
- [72. 如何实现一个简单的线程池?](#72. 如何实现一个简单的线程池?)
- [73. 如何实现一个简单的生产者-消费者模型?](#73. 如何实现一个简单的生产者-消费者模型?)
- [74. 如何实现一个简单的观察者模式(基于事件总线)?](#74. 如何实现一个简单的观察者模式(基于事件总线)?)
- [75. 如何实现一个简单的状态机?](#75. 如何实现一个简单的状态机?)
- [76. 如何实现一个简单的内存管理器?](#76. 如何实现一个简单的内存管理器?)
- [77. 如何实现一个简单的模板元编程示例?](#77. 如何实现一个简单的模板元编程示例?)
- [78. 如何实现一个简单的协程?](#78. 如何实现一个简单的协程?)
- [79. 如何实现一个简单的事件驱动系统?](#79. 如何实现一个简单的事件驱动系统?)
- [80. 如何实现一个简单的RAII资源管理器?](#80. 如何实现一个简单的RAII资源管理器?)
-
- 二、100道面试题目录列表
一、本文面试题目录
71. 如何实现一个简单的内存池?
答案 :
内存池预分配大块内存,减少频繁系统调用带来的开销,提高内存分配效率。
示例代码:
cpp
#include <vector>
#include <stack>
#include <cstddef>
class MemoryPool {
private:
std::vector<char*> blocks; // 存储分配的大块内存
std::stack<char*> freeChunks; // 存储空闲内存块
size_t chunkSize; // 每个内存块的大小
size_t blockSize; // 每次分配的大块内存大小
public:
MemoryPool(size_t chunkSize, size_t blockSize = 1024)
: chunkSize(chunkSize), blockSize(blockSize) {
allocateBlock();
}
~MemoryPool() {
for (char* block : blocks) {
delete[] block;
}
}
void* allocate() {
if (freeChunks.empty()) {
allocateBlock();
}
char* chunk = freeChunks.top();
freeChunks.pop();
return chunk;
}
void deallocate(void* ptr) {
if (ptr) {
freeChunks.push(static_cast<char*>(ptr));
}
}
private:
void allocateBlock() {
char* block = new char[chunkSize * blockSize];
blocks.push_back(block);
// 将大块内存分割成多个内存块并加入空闲列表
for (size_t i = 0; i < blockSize; ++i) {
freeChunks.push(block + i * chunkSize);
}
}
};
原理:
- 预分配大块内存,将其分割为固定大小的内存块。
- 使用栈管理空闲内存块,分配时直接从栈顶获取,释放时放回栈顶。
- 减少系统调用次数,降低内存碎片。
72. 如何实现一个简单的线程池?
答案 :
线程池维护固定数量的工作线程,避免频繁创建和销毁线程的开销,提高并发性能。
示例代码:
cpp
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <atomic>
class ThreadPool {
private:
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queueMutex;
std::condition_variable condition;
std::atomic<bool> stop;
public:
explicit ThreadPool(size_t numThreads) : stop(false) {
for (size_t i = 0; i < numThreads; ++i) {
workers.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(this->queueMutex);
this->condition.wait(lock, [this] {
return this->stop || !this->tasks.empty();
});
if (this->stop && this->tasks.empty())
return;
task = std::move(this->tasks.front());
this->tasks.pop();
}
task();
}
});
}
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queueMutex);
stop = true;
}
condition.notify_all();
for (std::thread& worker : workers) {
worker.join();
}
}
template<class F>
void enqueue(F&& f) {
{
std::unique_lock<std::mutex> lock(queueMutex);
if (stop)
throw std::runtime_error("enqueue on stopped ThreadPool");
tasks.emplace(std::forward<F>(f));
}
condition.notify_one();
}
};
原理:
- 预先创建固定数量的工作线程,线程不断从任务队列获取任务执行。
- 任务队列使用互斥锁和条件变量实现线程安全。
- 线程池析构时通知所有线程停止工作并等待它们完成。
73. 如何实现一个简单的生产者-消费者模型?
答案 :
生产者-消费者模型通过共享缓冲区解耦数据生产和消费,使用同步机制确保线程安全。
示例代码:
cpp
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
template<typename T>
class ProducerConsumerQueue {
private:
std::queue<T> queue;
std::mutex mutex;
std::condition_variable notEmpty;
std::condition_variable notFull;
size_t maxSize;
public:
explicit ProducerConsumerQueue(size_t size) : maxSize(size) {}
void produce(const T& item) {
std::unique_lock<std::mutex> lock(mutex);
notFull.wait(lock, [this] { return queue.size() < maxSize; });
queue.push(item);
notEmpty.notify_one();
}
T consume() {
std::unique_lock<std::mutex> lock(mutex);
notEmpty.wait(lock, [this] { return !queue.empty(); });
T item = queue.front();
queue.pop();
notFull.notify_one();
return item;
}
};
// 使用示例
void producer(ProducerConsumerQueue<int>& queue) {
for (int i = 0; i < 10; ++i) {
queue.produce(i);
std::cout << "Produced: " << i << std::endl;
}
}
void consumer(ProducerConsumerQueue<int>& queue) {
for (int i = 0; i < 10; ++i) {
int item = queue.consume();
std::cout << "Consumed: " << item << std::endl;
}
}
原理:
- 使用有界队列作为缓冲区,生产者向队列添加数据,消费者从队列取出数据。
- 两个条件变量分别用于等待队列非空(消费者)和队列非满(生产者)。
- 互斥锁保护队列的读写操作,确保线程安全。
74. 如何实现一个简单的观察者模式(基于事件总线)?
答案 :
事件总线模式是观察者模式的扩展,通过中央事件管理器解耦发布者和订阅者。
示例代码:
cpp
#include <unordered_map>
#include <vector>
#include <functional>
#include <mutex>
class EventBus {
private:
using EventHandler = std::function<void(const void*)>;
std::unordered_map<std::string, std::vector<EventHandler>> subscribers;
std::mutex mutex;
public:
void subscribe(const std::string& eventType, EventHandler handler) {
std::lock_guard<std::mutex> lock(mutex);
subscribers[eventType].push_back(handler);
}
void publish(const std::string& eventType, const void* data = nullptr) {
std::lock_guard<std::mutex> lock(mutex);
auto it = subscribers.find(eventType);
if (it != subscribers.end()) {
for (const auto& handler : it->second) {
handler(data);
}
}
}
};
// 使用示例
struct UserLoggedInEvent {
std::string username;
};
void onUserLoggedIn(const void* data) {
const auto* event = static_cast<const UserLoggedInEvent*>(data);
std::cout << "User " << event->username << " logged in." << std::endl;
}
// 注册事件
EventBus bus;
bus.subscribe("UserLoggedIn", onUserLoggedIn);
// 发布事件
UserLoggedInEvent event{"john_doe"};
bus.publish("UserLoggedIn", &event);
原理:
- 事件总线维护事件类型到处理函数的映射表。
- 订阅者向总线注册对特定事件的处理函数。
- 发布者通过总线发布事件,总线调用所有注册的处理函数。
- 使用互斥锁确保线程安全。
75. 如何实现一个简单的状态机?
答案 :
状态机根据当前状态和输入执行状态转换,实现复杂的逻辑控制。
示例代码:
cpp
#include <unordered_map>
#include <functional>
#include <string>
#include <iostream>
class StateMachine {
public:
using State = std::string;
using Event = std::string;
using Action = std::function<void()>;
void addTransition(const State& from, const Event& event, const State& to, Action action = nullptr) {
transitions[from][event] = {to, action};
}
void processEvent(const Event& event) {
auto stateIt = transitions.find(currentState);
if (stateIt != transitions.end()) {
auto eventIt = stateIt->second.find(event);
if (eventIt != stateIt->second.end()) {
const Transition& transition = eventIt->second;
if (transition.action) {
transition.action();
}
currentState = transition.to;
std::cout << "State changed to: " << currentState << std::endl;
return;
}
}
std::cout << "Invalid event: " << event << " in state: " << currentState << std::endl;
}
void setInitialState(const State& state) {
currentState = state;
}
private:
struct Transition {
State to;
Action action;
};
State currentState;
std::unordered_map<State, std::unordered_map<Event, Transition>> transitions;
};
原理:
- 使用嵌套哈希表存储状态转换规则(当前状态→事件→目标状态+动作)。
processEvent()根据当前状态和事件查找转换规则,执行动作并更新状态。- 状态机可用于游戏AI、工作流引擎等场景。
76. 如何实现一个简单的内存管理器?
答案 :
内存管理器管理动态内存分配,优化内存使用并减少碎片。
示例代码:
cpp
#include <cstddef>
#include <cstdlib>
#include <vector>
#include <algorithm>
class MemoryManager {
private:
struct Block {
size_t size;
bool free;
Block* next;
};
Block* head;
size_t totalMemory;
public:
explicit MemoryManager(size_t size) : totalMemory(size) {
head = static_cast<Block*>(std::malloc(sizeof(Block) + size));
head->size = size;
head->free = true;
head->next = nullptr;
}
~MemoryManager() {
std::free(head);
}
void* allocate(size_t size) {
Block* current = head;
while (current) {
if (current->free && current->size >= size) {
// 分割块
if (current->size > size + sizeof(Block)) {
Block* newBlock = reinterpret_cast<Block*>(reinterpret_cast<char*>(current) + sizeof(Block) + size);
newBlock->size = current->size - size - sizeof(Block);
newBlock->free = true;
newBlock->next = current->next;
current->next = newBlock;
current->size = size;
}
current->free = false;
return reinterpret_cast<char*>(current) + sizeof(Block);
}
current = current->next;
}
return nullptr; // 内存不足
}
void deallocate(void* ptr) {
if (!ptr) return;
Block* block = reinterpret_cast<Block*>(reinterpret_cast<char*>(ptr) - sizeof(Block));
block->free = true;
mergeBlocks();
}
private:
void mergeBlocks() {
Block* current = head;
while (current && current->next) {
if (current->free && current->next->free) {
current->size += current->next->size + sizeof(Block);
current->next = current->next->next;
} else {
current = current->next;
}
}
}
};
原理:
- 使用空闲链表管理内存块,每个块包含大小、状态和指向下一块的指针。
- 分配时查找足够大的空闲块,可选择分割。
- 释放时标记块为空闲并合并相邻空闲块,减少碎片。
77. 如何实现一个简单的模板元编程示例?
答案 :
模板元编程(TMP)在编译期执行计算,将运行时开销转移到编译期。
示例代码(编译期斐波那契数列):
cpp
template<int N>
struct Fibonacci {
static constexpr int value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};
// 特化终止条件
template<>
struct Fibonacci<0> {
static constexpr int value = 0;
};
template<>
struct Fibonacci<1> {
static constexpr int value = 1;
};
// 使用示例
int main() {
constexpr int result = Fibonacci<5>::value; // 编译期计算斐波那契数列第5项(值为5)
return 0;
}
原理:
- 模板递归实例化实现编译期计算。
- 特化终止递归,避免无限展开。
- 计算结果存储在静态常量中,运行时直接使用,无计算开销。
78. 如何实现一个简单的协程?
答案 :
协程允许函数在执行过程中暂停和恢复,保存上下文状态。
示例代码(简化版协程框架):
cpp
#include <iostream>
#include <functional>
#include <vector>
class Coroutine {
public:
using Task = std::function<void()>;
enum class State {
READY,
RUNNING,
SUSPENDED,
FINISHED
};
explicit Coroutine(Task task) : task(task), state(State::READY) {}
void resume() {
if (state == State::READY || state == State::SUSPENDED) {
state = State::RUNNING;
if (!callStack.empty()) {
// 恢复执行
callStack.back()();
} else {
// 首次执行
task();
}
if (state == State::RUNNING) {
state = State::FINISHED;
}
}
}
void yield() {
if (state == State::RUNNING) {
state = State::SUSPENDED;
// 使用longjmp/setjmp或context切换技术(此处简化)
// 实际实现需要保存寄存器、栈等上下文
}
}
State getState() const {
return state;
}
private:
Task task;
State state;
std::vector<Task> callStack; // 简化的调用栈
};
原理:
- 协程通过保存和恢复执行上下文实现暂停和继续。
yield()保存当前状态并暂停执行,resume()恢复状态继续执行。- 实际实现需使用平台相关的上下文切换技术(如
ucontext.h或汇编)。
79. 如何实现一个简单的事件驱动系统?
答案 :
事件驱动系统通过事件解耦组件,提高系统灵活性和可维护性。
示例代码:
cpp
#include <unordered_map>
#include <vector>
#include <functional>
#include <mutex>
class EventSystem {
public:
using EventHandler = std::function<void(const std::vector<int>&)>;
void subscribe(const std::string& eventType, EventHandler handler) {
std::lock_guard<std::mutex> lock(mutex);
subscribers[eventType].push_back(handler);
}
void unsubscribe(const std::string& eventType, EventHandler handler) {
std::lock_guard<std::mutex> lock(mutex);
auto it = subscribers.find(eventType);
if (it != subscribers.end()) {
auto& handlers = it->second;
handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end());
}
}
void publish(const std::string& eventType, const std::vector<int>& args = {}) {
std::lock_guard<std::mutex> lock(mutex);
auto it = subscribers.find(eventType);
if (it != subscribers.end()) {
for (const auto& handler : it->second) {
handler(args);
}
}
}
private:
std::unordered_map<std::string, std::vector<EventHandler>> subscribers;
std::mutex mutex;
};
原理:
- 事件系统维护事件类型到处理函数的映射。
- 组件订阅感兴趣的事件,发布者通过系统发布事件。
- 事件触发时,系统调用所有注册的处理函数。
- 使用互斥锁确保线程安全。
80. 如何实现一个简单的RAII资源管理器?
答案 :
RAII(资源获取即初始化)通过对象生命周期管理资源,确保资源正确释放。
示例代码:
cpp
#include <iostream>
#include <functional>
template<typename T, typename D>
class ResourceManager {
private:
T resource;
D deleter;
bool valid;
public:
explicit ResourceManager(T res, D del) : resource(res), deleter(del), valid(true) {}
~ResourceManager() {
if (valid) {
deleter(resource);
}
}
// 禁止拷贝
ResourceManager(const ResourceManager&) = delete;
ResourceManager& operator=(const ResourceManager&) = delete;
// 允许移动
ResourceManager(ResourceManager&& other) noexcept :
resource(other.resource), deleter(other.deleter), valid(other.valid) {
other.valid = false;
}
ResourceManager& operator=(ResourceManager&& other) noexcept {
if (this != &other) {
if (valid) {
deleter(resource);
}
resource = other.resource;
deleter = other.deleter;
valid = other.valid;
other.valid = false;
}
return *this;
}
T get() const {
return resource;
}
};
// 使用示例
void* allocateMemory(size_t size) {
return std::malloc(size);
}
void freeMemory(void* ptr) {
std::free(ptr);
}
int main() {
ResourceManager<void*, decltype(&freeMemory)> memory(allocateMemory(1024), freeMemory);
// 内存会在memory对象析构时自动释放
return 0;
}
原理:
- 构造时获取资源,析构时释放资源。
- 禁用拷贝构造和赋值,防止资源重复释放。
- 通过移动语义转移资源所有权。
- 适用于文件句柄、网络连接、内存等资源管理。