一、右值引用与移动语义:性能革命的核心
面试真题(字节跳动)
"如何实现高效字符串拼接?解释std::move原理及适用场景"
1. 核心概念
-
左值:具名对象,可取地址(如变量、函数返回值)
-
右值:临时对象,无持久身份(如字面量、表达式结果)
// 左值引用
void process(std::string& s); // 右值引用(C++11)
void process(std::string&& s);
2. 移动语义实现
class Vector {
public: // 移动构造函数 Vector(Vector&& other) noexcept : data_(other.data_), size_(other.size_) { other.data_ = nullptr; // 源对象置空 other.size_ = 0; } // 移动赋值运算符 Vector& operator=(Vector&& other) noexcept { if (this != &other) { delete[] data_; data_ = other.data_; size_ = other.size_; other.data_ = nullptr; other.size_ = 0; } return *this; } private: int* data_; size_t size_;
};
优化场景:容器扩容、函数返回大对象(编译器自动应用RVO/NRVO)
二、智能指针:根治内存泄漏的利器
面试真题(腾讯)
"shared_ptr线程安全吗?weak_ptr如何解决循环引用?"
1. 三大智能指针对比
| 类型 | 所有权 | 线程安全 | 适用场景 |
|---|---|---|---|
unique_ptr | 独占 | 单线程安全 | 资源独占管理 |
shared_ptr | 共享 | 引用计数原子 | 共享资源 |
weak_ptr | 观测 | 非线程安全 | 打破循环引用 |
2. 循环引用解决方案
class B; class A {
public: std::shared_ptr<B> b_ptr;
}; class B {
public: std::weak_ptr<A> a_ptr; // 关键:使用weak_ptr打破循环
}; // 使用
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a; // 无内存泄漏!
3. 大厂编码规范
-
禁止使用
new/delete(阿里C++规范) -
工厂函数返回
unique_ptr(Google Style Guide) -
跨模块传递使用
shared_ptr(腾讯跨线程资源管理)
三、Lambda表达式:函数式编程的钥匙
面试真题(快手)
"实现按字符串长度排序,捕获列表[&]和[=]有何风险?"
1. 核心语法
auto lambda = [capture](params) mutable -> retType { // 函数体
};
2. 捕获方式对比
| 捕获方式 | 效果 | 风险 |
|---|---|---|
[&] | 引用捕获所有变量 | 悬空引用(对象已销毁) |
[=] | 值捕获所有变量 | 性能损耗(大对象拷贝) |
[this] | 捕获当前类成员 | 类销毁后访问导致崩溃 |
[x, &y] | 混合捕获(x值捕获,y引用捕获) | 精准控制 |
安全实践:
// 明确列出捕获变量
std::vector<std::string> names;
std::sort(names.begin(), names.end(), [](const auto& a, const auto& b) { return a.size() < b.size(); // 无捕获,最安全 }
);
四、并发编程:锁与线程池实战
面试真题(阿里)
"手写线程安全队列,并说明锁粒度优化策略"
1. 锁的进化史
| 工具 | 特性 | 适用场景 |
|---|---|---|
std::mutex | 基础互斥锁 | 简单临界区 |
lock_guard | RAII封装(C++11) | 自动释放 |
unique_lock | 灵活锁定(C++11) | 条件变量配合 |
shared_mutex | 读写分离(C++17) | 读多写少场景 |
2. 线程安全队列实现
template<typename T>
class SafeQueue {
public: void push(T value) { std::lock_guard<std::mutex> lock(mutex_); queue_.push(std::move(value)); cond_.notify_one(); } bool try_pop(T& value) { std::lock_guard<std::mutex> lock(mutex_); if (queue_.empty()) return false; value = std::move(queue_.front()); queue_.pop(); return true; } private: std::queue<T> queue_; mutable std::mutex mutex_; std::condition_variable cond_;
};
3. 线程池核心设计(网易云音乐实践)
class ThreadPool {
public: explicit ThreadPool(size_t threads) { for (size_t i = 0; i < threads; ++i) { workers_.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(queue_mutex_); condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); }); if (stop_ && tasks_.empty()) return; task = std::move(tasks_.front()); tasks_.pop(); } task(); } }); } } // 添加任务接口 template<class F> void enqueue(F&& f) { { std::lock_guard<std::mutex> lock(queue_mutex_); tasks_.emplace(std::forward<F>(f)); } condition_.notify_one(); } ~ThreadPool() { { std::lock_guard<std::mutex> lock(queue_mutex_); stop_ = true; } condition_.notify_all(); for (std::thread& worker : workers_) { worker.join(); } }
};
五、C++14/17里程碑特性
面试真题(拼多多)
*"用constexpr实现编译期斐波那契数列,C++14做了哪些改进?"*
1. C++14核心升级
-
泛型Lambda:
auto print = [](const auto& x) { std::cout << x; }; constexpr扩展:cpp复制下载constexpr int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } static_assert(factorial(5) == 120); // 编译期计算
2. C++17革命性特性
-
结构化绑定:
std::map<int, std::string> m; for (const auto& [key, value] : m) { // 直接解包 // 使用key和value } -
std::optional防空指针:std::optional<int> find(int id) { if (id == 42) return 42; return std::nullopt; // 明确表示无值 } -
并行STL(字节跳动优化案例):
std::vector<int> data(1000000); std::sort(std::execution::par, data.begin(), data.end()); // 并行排序
六、内存泄漏防御体系
面试真题(网易)
"Valgrind如何检测内存泄漏?AddressSanitizer原理是什么?"
1. 编码层防御
-
RAII终极法则:
{ auto file = std::fstream("data.txt"); // 退出作用域自动关闭 auto mem = std::make_unique<char[]>(1024); // 自动释放内存 } // 资源自动释放 -
避免裸指针所有权传递
2. 工具层检测
| 工具 | 原理 | 优势 |
|---|---|---|
| Valgrind | 动态二进制插桩 | 无需重编译 |
| AddressSanitizer | 影子内存映射 | 性能损耗低(<2x) |
| LeakSanitizer | 专精泄漏检测 | 集成于ASan |
阿里实践:CI流水线强制开启ASan检测
七、经典算法实战(附解题策略)
面试真题(字节/腾讯)
"int数组中仅一个数出现1次,其余出现n次,找出该数"
解法1:位运算(n=2时)
int singleNumber(vector<int>& nums) { int res = 0; for (int num : nums) res ^= num; return res;
}
解法2:通用数学公式(n任意)
int findUnique(vector<int>& nums, int n) { int res = 0; for (int i = 0; i < 32; ++i) { int sum = 0; for (int num : nums) { sum += (num >> i) & 1; } if (sum % n != 0) { res |= (1 << i); } } return res;
}
复杂度:O(32n) → 高效处理海量数据
八、初始化顺序:从源码到二进制
面试真题(腾讯)
"全局变量、静态局部变量、类静态成员初始化顺序?"
C++对象生命周期图谱
| 变量类型 | 初始化时机 | 销毁时机 |
|---|---|---|
| 全局变量 | main()之前 | main()之后 |
| 静态全局变量 | main()之前 | main()之后 |
| 类静态成员 | 首次使用时(线程安全) | main()之后 |
| 静态局部变量 | 首次执行到声明处 | main()之后 |
| 线程局部存储 | 线程启动时 | 线程结束时 |
阿里编码规约:避免静态变量相互依赖!
九、虚函数深度探秘
面试真题(阿里)
"纯虚函数子类必须实现吗?抽象类可以有数据成员吗?"
核心规则
-
纯虚函数:
class Shape { public: virtual void draw() = 0; // 纯虚函数 };-
子类必须实现所有纯虚函数,否则仍是抽象类
-
抽象类可以包含数据成员和普通成员函数
-
陷阱案例
class Derived : public Shape {
public: // 未实现draw() → 编译错误!
};
十、现代C++工程实践
1. 预防内存泄漏
-
智能指针全覆盖:替换所有new/delete
-
资源类禁用拷贝:
class Socket { public: Socket() = default; ~Socket() { close(fd_); } // 禁用拷贝 Socket(const Socket&) = delete; Socket& operator=(const Socket&) = delete; // 启用移动 Socket(Socket&&) noexcept; Socket& operator=(Socket&&) noexcept; };
2. 高性能线程池优化(腾讯会议实践)
-
任务窃取(Work-Stealing):避免线程饥饿
-
无锁队列:减少锁竞争(使用atomic实现)
-
本地任务缓存:L1缓存亲和性优化
十一、补充核心特性深度解析
1. 变长模板(Variadic Templates)
面试真题(阿里):"实现类型安全的printf"
void safe_printf(const char* s) { while (*s) { if (*s == '%' && *(++s) != '%') throw std::runtime_error("invalid format"); std::cout << *s++; }
} template<typename T, typename... Args>
void safe_printf(const char* s, T value, Args... args) { while (*s) { if (*s == '%' && *(++s) != '%') { std::cout << value; return safe_printf(++s, args...); } std::cout << *s++; } throw std::runtime_error("extra arguments");
}
2. 委托构造函数(C++11)
面试真题(腾讯):"解释构造函数链式调用优势"
class Config {
public: Config() : Config("default", 8080) {} // 委托构造 Config(std::string name) : Config(name, 8080) {} Config(std::string name, int port) : name_(std::move(name)), port_(port) {}
private: std::string name_; int port_;
};
3. 文件系统库(C++17)
面试真题(网易):"递归遍历目录统计文件大小"
namespace fs = std::filesystem; uintmax_t dir_size(const fs::path& dir) { uintmax_t size = 0; for (const auto& entry : fs::recursive_directory_iterator(dir)) { if (entry.is_regular_file()) size += entry.file_size(); } return size;
}
4. 折叠表达式(C++17)
面试真题(字节):"实现编译期类型列表判断"
template<typename T, typename... Args>
constexpr bool contains_v = (std::is_same_v<T, Args> || ...); static_assert(contains_v<int, char, double, int>); // true
5. if constexpr(C++17)
面试真题(拼多多):"实现类型特化的通用处理"
template<typename T>
auto process(T value) { if constexpr (std::is_pointer_v<T>) { return *value; // 指针解引用 } else { return value; // 直接返回值 }
}
十二、内存模型与原子操作
面试真题(阿里):"解释memory_order_relaxed与seq_cst区别"
内存序级别
| 内存序 | 特性 | 性能 |
|---|---|---|
memory_order_relaxed | 仅保证原子性 | 最高 |
memory_order_consume | 数据依赖顺序 | 高 |
memory_order_acquire | 读操作后的指令不能重排 | 中 |
memory_order_release | 写操作前的指令不能重排 | 中 |
memory_order_acq_rel | acquire+release组合 | 中低 |
memory_order_seq_cst | 全局顺序一致性(默认) | 最低 |
腾讯实践:无锁队列使用acquire-release模型
十三、实战:游戏资源加载系统
多线程资源管理器
class ResourceManager {
public: void load_async(const std::string& path) { std::lock_guard lock(mutex_); futures_.emplace_back(std::async(std::launch::async, [=] { auto res = load_resource(path); std::lock_guard lock(mutex_); resources_[path] = res; })); } std::shared_ptr<Texture> get(const std::string& path) { std::lock_guard lock(mutex_); if (auto it = resources_.find(path); it != resources_.end()) return it->second; return nullptr; } private: std::mutex mutex_; std::unordered_map<std::string, std::shared_ptr<Texture>> resources_; std::vector<std::future<void>> futures_;
};
共享内存通信(Unity-C++交互)
// C++ 进程
int shm_fd = shm_open("/game_data", O_CREAT | O_RDWR, 0666);
ftruncate(shm_fd, sizeof(GameState));
GameState* state = static_cast<GameState*>( mmap(NULL, sizeof(GameState), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)); // Unity C#
[DllImport("libc")]
private static extern IntPtr shm_open(string name, int flags, int mode); void* ptr = shm_open("/game_data", O_RDONLY, 0);
GameState state = Marshal.PtrToStructure<GameState>(ptr);
十四、大厂编码规范与最佳实践
1. 腾讯C++规范
-
禁止使用C风格字符串(char*)
-
所有容器必须预分配内存(reserve)
-
跨线程传递必须使用shared_ptr/weak_ptr
2. 阿里性能优化条例
-
热点循环避免虚函数调用
-
数据结构按缓存行对齐(alignas(64))
-
高频调用函数强制inline
3. 字节安全编程准则
-
所有用户输入必须验证
-
敏感操作必须双因子校验
-
内存操作必须边界检查
十五、C++20前瞻特性
1. 概念(Concepts)
template<typename T>
concept Drawable = requires(T t) { t.draw(); // 必须实现draw方法
}; template<Drawable T>
void render(T&& obj) { obj.draw();
}
2. 协程(Coroutines)
task<int> async_compute() { int result = co_await async_operation(); co_return result * 2;
}
3. 范围库(Ranges)
auto even_squares = views::iota(1) | views::filter([](int i){ return i % 2 == 0; }) | views::transform([](int i){ return i * i; }) | views::take(10);
结语:C++新标准不仅是语法糖,更是工程思维的进化。掌握右值引用、智能指针、并发工具等特性,结合RAII等核心范式,方能构建高性能、零泄漏的现代C++系统。本文涵盖超30道大厂真题及实战代码,助你在面试中游刃有余。