Appearance
现代 C++
C++11 常用特性
auto与decltype类型推导;- 基于范围的
for; - Lambda 表达式与闭包;
- 右值引用、移动语义、完美转发;
std::unique_ptr、std::shared_ptr、std::weak_ptr;nullptr取代宏NULL;constexpr、static_assert、强类型枚举enum class;- 并发库:
std::thread、mutex、condition variable、atomic、future; - 统一初始化、委托构造、
override/final。
左值、右值与引用
现代 C++ 中不应仅按“有名字就是左值、临时就是右值”死记;更精确地说,表达式有 glvalue、prvalue、xvalue 等分类。但面试可先掌握:
- **左值(lvalue)**通常表示有持久身份、可定位的对象;左值引用写作
T&。 - **右值(rvalue)**通常表示即将消亡的对象或纯值;右值引用写作
T&&,可绑定临时对象。 - 命名的右值引用变量本身是左值表达式,要把它作为可移动对象继续传递,通常使用
std::move。
cpp
std::string make_name();
const std::string& a = make_name(); // 临时对象生命周期延长到 a 的作用域结束
std::string&& b = make_name(); // 直接绑定时也延长生命周期引用本身不是所有权。不要让引用或指针逃逸到被引用对象生命周期之外。
移动语义
复制大型资源(堆内存、文件句柄、网络连接等)可能昂贵。移动语义允许目标对象接管源对象的资源,而不是复制资源内容:
cpp
class Buffer {
public:
Buffer(Buffer&& other) noexcept
: data_(std::exchange(other.data_, nullptr)) {}
private:
int* data_{};
};移动构造函数和移动赋值接收 T&&。移动后源对象必须保持有效但状态未指定,不能假设它为空或不可用;可以重新赋值或销毁。std::move 不移动任何数据,它只是把表达式转换为可绑定右值引用的 xvalue,真正是否移动由被调用函数决定。
为移动操作标注 noexcept 很重要:例如 vector 扩容时,若移动可能抛异常,为保持强异常安全保证,容器可能选择拷贝而非移动。
Lambda 表达式
基本形式:
cpp
[capture](parameters) mutable noexcept -> return_type {
// body
};常见捕获:
cpp
int threshold = 10;
[threshold](int x) { return x > threshold; }; // 值捕获:副本
[&threshold](int x) { ++threshold; return x > threshold; }; // 引用捕获
[x = std::move(resource)] { /* 初始化捕获,转移资源 */ };- 默认
[]的operator()是const;若需修改值捕获副本,使用mutable。 - 引用捕获的 lambda 若被异步保存/返回,必须确保被引用对象仍存活,避免悬空引用。
- Lambda 常用于 STL 算法、回调、异步任务和局部策略表达。无捕获 Lambda 可转换为函数指针;有捕获 Lambda 是闭包对象。
面试速答
移动语义的作用:针对临时对象或不再需要原资源的对象,把资源所有权转交给目标,避免深拷贝,提升容器扩容、函数返回和资源包装类型的性能。前提是正确维护源对象有效性、异常安全和所有权语义。