高级 C++ 特性和现代 C++
解释 C++11 及更高版本中 std::move 和 std::forward 的目的。
回答:
std::move 无条件地将其参数转换为右值引用(rvalue reference),从而启用移动语义(转移资源所有权)。std::forward 根据原始参数是否为右值来有条件地将其参数转换为右值引用,在完美转发(perfect forwarding)场景中保留其值类别(value categories)。
什么是 C++ 中的零法则、三法则和五法则?
回答:
三法则(Rule of Three)指出,如果你定义了析构函数、拷贝构造函数或拷贝赋值运算符中的任何一个,你就应该定义全部三个。五法则(Rule of Five)将其扩展到包括移动构造函数和移动赋值运算符。零法则(Rule of Zero)建议,如果你的类不直接管理资源,则不应定义任何这些函数,而应依赖编译器生成的默认版本或智能指针。
描述“完美转发”(perfect forwarding)的概念以及 std::forward 如何实现它。
回答:
完美转发允许函数模板接受任意参数,并将它们转发给另一个函数,同时保留其原始值类别(左值 lvalue 或右值 rvalue)和 const/volatile 限定符。std::forward 对此至关重要,因为它仅在原始参数为右值时才将其有条件地转换为右值引用,从而确保转发调用能够正确解析重载。
什么是智能指针(std::unique_ptr、std::shared_ptr、std::weak_ptr)以及为什么它们比原始指针更受青睐?
回答:
智能指针是原始指针的 RAII(Resource Acquisition Is Initialization)包装器,它们自动管理内存,防止内存泄漏和悬空指针(dangling pointers)。unique_ptr 提供独占所有权,shared_ptr 通过引用计数实现共享所有权,而 weak_ptr 则用于打破 shared_ptr 循环中的循环引用。它们简化了资源管理并提高了代码安全性。
解释 C++ 中 noexcept 和 throw() 的区别。
回答:
throw()(在 C++11 中已弃用)是一种动态异常规范,它在运行时检查是否抛出了未列出的异常,从而导致 std::unexpected。noexcept(C++11 及更高版本)是一种编译时规范,指示函数不会抛出异常。如果 noexcept 函数确实抛出了异常,则会调用 std::terminate,从而为优化提供更强的保证。
C++11 中的 lambda 表达式是什么?它的主要组成部分有哪些?
回答:
Lambda 表达式是一个匿名函数对象,可以在原地定义。它的主要组成部分是捕获子句(capture clause,[])、参数列表(parameter list,())、mutable 规范(可选)、异常规范(exception specification,可选)、返回类型(return type,可选,会自动推导)和函数体(function body,{})。Lambda 表达式对于简洁的回调函数和算法非常有用。
C++ 中的 const 和 constexpr 有何不同?
回答:
const 表示变量的值在初始化后不能被更改,或者成员函数不修改对象的状态。constexpr(C++11 及更高版本)表示一个值或函数可以在编译时进行求值。对于变量,constexpr 暗示 const,但 const 不暗示 constexpr。
什么是 SFINAE(Substitution Failure Is Not An Error)以及如何使用它?
回答:
SFINAE 是 C++ 模板元编程中的一个原则,即如果模板实例化在模板参数替换过程中失败,则不是错误,而是该特定重载或特化从候选集中移除。它通常与 std::enable_if 一起使用,根据类型特征(type traits)有条件地启用或禁用模板实例化。
解释 C++11 中“可变参数模板”(variadic templates)的概念。
回答:
可变参数模板是能够接受可变数量参数的模板。它们使用参数包(parameter packs,typename... Args 或 Args...)来表示零个或多个模板参数或函数参数的序列。它们通常通过递归处理或使用折叠表达式(fold expressions,C++17)来对参数包中的每个元素进行操作。
什么是“右值引用”(rvalue references)以及它们如何实现“移动语义”(move semantics)?
回答:
右值引用(&&)仅绑定到右值(临时对象或即将被销毁的对象),这使它们与左值引用(&)区分开来。这种区别允许编译器选择那些可以“窃取”临时对象资源的重载(移动构造函数/赋值运算符),而不是执行昂贵的深拷贝,从而实现移动语义并提高性能。
描述 C++17 中 std::optional、std::variant 和 std::any 的目的。
回答:
std::optional 表示一个可选值,可以包含一个值或为空,对于可能不返回结果的函数很有用。std::variant 是一个类型安全的联合体(union),在任何给定时间保存一组指定类型中的一个。std::any 可以保存任何单个类型的值,提供类型安全的异构存储(heterogeneous storage),类似于 void 指针但带有类型信息。