(嵌入式八股)第3章 C++11(一)

3.1 NULL 和 nullptr 的区别

NULLnullptr 都用于表示空指针,但它们在类型、使用场景和安全性上存在一些区别。

类型区别:

  • NULL 是宏定义,通常定义为 0,它是一个整数类型,表示空指针。
  • nullptr 是 C++11 引入的关键字,表示一个专门用于指针类型的空值。

安全性区别:

  • NULL 作为宏定义,它可能被错误地解释为整型 0,因此在某些情况下可能会导致二义性问题。
  • nullptr 是一个专门的空指针类型,类型为 std::nullptr_t,因此避免了 NULL 所引起的二义性。

上下文匹配:

  • NULL 可以在整型上下文中使用(例如传递给整型函数),但 nullptr 只能用于指针上下文。

例子(NULL二义性报错):

例子(nullptr消除二义性):

  • 要解决这个问题,可以使用 nullptr 来消除二义性。nullptr 是 C++11 引入的,表示空指针,并且它具有明确的类型 std::nullptr_t,避免了与整型 0 的混淆。
  • 在调用 func() 时,使用 nullptr 替换 NULL
  • 确保函数的重载能够明确区分类型。
  • 总结

    • NULL 是一个整数值(通常为 0),在 C++ 中可能会导致一些二义性问题,特别是在函数重载和模板编程中。
    • nullptr 是 C++11 引入的一个专门用于表示空指针的关键字,它的类型是 std::nullptr_t,避免了 NULL 所引起的类型问题。
    • nullptr 在函数重载等场景中更安全、更清晰,因为它不容易被误解释为整数类型。

    3.2 auto 类型推导

    auto 是一个非常有用的关键字,用于自动推导变量的类型。通过 auto,编译器会根据变量初始化时的表达式类型来推导变量的类型。这使得编程更加简洁,尤其是当类型非常复杂或冗长时。

    基本语法

  • variable_name:变量名
  • expression:初始化表达式,编译器根据此表达式推导出变量的类型
  • auto 的优点

    • 简化代码:对于复杂的类型声明,auto 可以避免重复写出复杂的类型,增强代码可读性。
    • 避免类型错误:通过 auto,可以减少手动写出类型时发生类型错误的风险,尤其在使用模板和 STL 容器时。
    • 适用于所有类型:可以用于推导基本数据类型、指针类型、引用类型,甚至是模板类型。

    使用 auto 的示例

    示例 1:基本数据类型

    变量 a 被自动推导为 int 类型,b 被推导为 double 类型。

    示例 2:使用 auto 与指针

    变量 ptr 被自动推导为 int* 类型,并指向变量 x

    示例 3:使用 auto 与引用

    变量 ref 被自动推导为 int& 类型,表示 x 的引用。修改 ref 也修改了 x 的值。

    示例 4:auto 与容器迭代器

    在使用 STL 容器时,auto 是非常有用的,尤其是容器的迭代器类型较为复杂。

    使用 auto 让编译器推导出 it 的类型为 vector<int>::iterator,避免手动写出复杂的类型。

    限制与注意事项

    • auto 必须初始化:编译器根据初始化表达式推导类型,因此必须在声明时进行初始化。
    • 不能与 constvolatile 等修饰符混用:如果需要修饰变量为常量引用,应该显式地使用 const

    autodecltype 区别

  • auto 根据初始化表达式推导类型。
  • decltype 根据给定表达式的类型来推导变量类型,decltype 不要求初始化。
  • 3.3 decltype 类型推导

    decltype 是 一个关键字,用于推导表达式的类型。与 auto 不同,decltype 关注的是 表达式的类型,而 auto 根据 初始化值 来推导类型。decltype 可以非常精确地得到一个表达式的类型,并且它不会计算表达式的值。

    decltype 简介

    decltype 用于根据表达式的类型推导变量类型,且不会实际计算表达式的值。其语法如下:

    说明:

    • expression 是一个表达式,编译器会根据这个表达式来推导出它的类型。
    • variable_name 是变量名,decltype 会推导出该变量的类型。

  • decltype(x)会推导出x的类型,假设xint类型,那么y的类型也将是int
  • typeid(y).name()会返回一个表示y类型的名称的字符串,尽管不同的编译器返回的类型名称格式可能不同,但通常会类似于“int”这种形式。在某些编译器中,它可能返回“i”而不是“int”。
  • decltypeauto 的区别

    decltype 推导规则

    普通表达式或变量

    • 如果表达式不是左值,decltype 返回表达式的类型。

    函数调用

    • 如果表达式是一个函数调用,decltype 返回该函数的返回值类型。

    左值

    • 如果表达式是左值(例如变量或数组元素),decltype 返回表达式的引用类型。

    在上述示例中,decltype((x)) 会推导出 x 的引用类型(int&),因为 (x) 是一个左值表达式。

    decltype 示例代码

    示例 1:基础类型推导

    • decltype(x) 将推导出 x 的类型(int),因此 y 被声明为 int 类型。

    示例 2:函数返回类型推导

    • decltype(add(1, 2)) 将推导出 add 函数的返回值类型(int),因此 result 的类型是 int

    示例 3:左值推导

  • decltype(x) 返回 int 类型。
  • decltype((x)) 返回 int& 类型,因为 (x) 是一个左值。
  • 总结

    • decltype 用于推导表达式的类型,而 auto 根据初始化值推导变量的类型。
    • decltype 不会计算表达式的值,仅通过类型推导来获取变量的类型。
    • decltype 可以处理各种复杂的表达式,特别是在模板编程中非常有用。

    3.4 override 和 final 关键字

    在 C++11 中,overridefinal 是两个非常有用的关键字,它们用于增强代码的可读性和安全性,特别是在处理继承和虚函数时。

    override 关键字

    override 关键字用于显式地指示派生类中的成员函数是对基类中的虚函数的重写。使用 override 可以提高代码的可读性,并确保派生类的函数正确地覆盖了基类中的虚函数。

    作用

    1. 明确重写override 使得代码更清晰,表明该函数确实是重写了基类的虚函数。
    2. 编译时检查:使用 override 时,编译器会确保派生类的函数签名(包括返回类型、参数类型等)与基类的虚函数完全匹配。如果签名不匹配,编译器会报错,从而避免因函数签名错误而导致的错误。

    • Derived 类中的 show() 函数使用 override 显式标明是重写了基类的 show() 函数。
    • 如果 Derived 类中的 show() 函数的签名不匹配基类中的 show(),编译器将报告错误。

    错误示例:

    final 关键字

    final 关键字用于指示一个类或虚函数不能被继承或重写。它有两个主要用途:

    1. 标记类不能被继承:如果一个类被声明为 final,则无法在该类的基础上创建派生类。
    2. 标记虚函数不能被重写:如果一个虚函数被声明为 final,则派生类不能重写该函数。

    final 用于类:

    • FinalClass 被声明为 final,意味着它不能被任何类继承。
    • 如果你尝试从 FinalClass 继承(如 Derived 类所示),编译器会报错,因为 FinalClass 已经被标记为最终类,不能被继承。

    final 用于虚函数:

  • Base 类中的 show() 被声明为 final,表示该函数不能被派生类重写。
  • 尝试在 Derived 类中重写 show() 函数时,编译器会报错,因为该函数已经被标记为 final
  • override 与 final 的组合使用

    overridefinal 可以一起使用,用于标记一个函数既是重写基类函数,又是最终版本,不能被进一步重写。通常,final 用于防止继承和重写,override 用于清晰地表示函数的重写。

  • show() 是一个虚函数,允许派生类重写。
  • 基类没有使用 final,因为它需要允许派生类覆盖该函数。
  • show() 使用 override 显式标明它是重写了基类的虚函数。
  • show() 使用 final 标记,表示这个重写版本不能再被进一步重写。
  • 由于 Derived 类中的 show() 已被标记为 final,因此在 AnotherDerived 类中尝试重写 show() 时,编译器会报错,防止对 Derived 中的 show() 进行进一步重写
  • obj.show() 将调用 Derived 类中的 show(),输出 "Derived class"
  • 如果取消注释 AnotherDerived obj2; obj2.show();,编译器将报错,因为 AnotherDerived 中无法重写 Derived 类中的 show() 函数。
  • 总结

    • override 用于显式标明派生类中的成员函数是基类虚函数的重写,增强代码的可读性,并让编译器帮助检查函数签名是否匹配。
    • final 用于标记类或虚函数不能被继承或重写,增加代码的安全性,防止不小心的继承或重写。
    • 两者可以组合使用,确保函数既是重写的又是最终版本,不能被进一步修改。

    3.5 Lambda 表达式

    Lambda 表达式 是 C++11 引入的一种新特性,允许创建 匿名函数,通常用于 简化代码、提高可读性,并可在 函数内部 定义内联函数,而不需要单独定义函数。

    Lambda 表达式的基本语法

    Lambda 表达式的基本语法如下:

  • capture list(捕获列表):指定 Lambda 表达式可以访问的外部变量。
  • 剩余60%内容,订阅专栏后可继续查看/也可单篇购买

    作者简介:仅用几个月时间0基础天坑急转嵌入式开发,逆袭成功拿下华为、vivo、小米等15个offer,面试经验100+,收藏20+面经,分享求职历程与学习心得。 专栏内容:这是一份覆盖嵌入式求职过程中99%问题指南,详细讲解了嵌入式开发的学习路径、项目经验分享、简历优化技巧、面试心得及实习经验,从技术面,HR面,AI面,主管面,谈薪一站式服务,助你突破技术瓶颈、打破信息差,争取更多大厂offer。

    全部评论

    相关推荐

    点赞 评论 收藏
    分享
    评论
    5
    6
    分享

    创作者周榜

    更多
    牛客网
    牛客企业服务