C++ 面试八股 细节点 精选一

  • 封装、继承和多态

  • 封装(Encapsulation):封装是将数据和操作(方法)包装在一个单元(类)中,实现了信息隐藏、数据安全性和代码模块化的目标
  • 继承(Inheritance):继承允许一个类派生出子类,并从父类继承其属性和行为。实现代码重用、层次结构组织等目标
  • 多态(Polymorphism):多态允许使用一个基类类型的指针或引用来调用派生类对象的特定方法。实现动态绑定和多态性。
  • 析构函数

  • 资源释放:析构函数可以用来释放对象所占有的资源。
  • 清理操作:如果对象在创建过程中进行了一些初始化操作,在析构函数中可以进行相应的清理操作,将对象恢复到初始状态。
  • 继承关系下的调用顺序:当存在继承关系时,派生类的析构函数会自动调用基类的析构函数,以便逐层清理各个父类的资源。
  • const关键字在C++

  • 声明常量变量 函数参数中的常量 常量引用 常成员函数
  • 引用(Reference)与指针(Pointer

  • 初始化:引用必须在声明时进行初始化,并且一旦初始化后不能改变其绑定的对象;指针可以在任何时候进行初始化,并且可以修改指向的对象。
  • 空值(null):指针可以具有空值(nullptr),表示未指向任何有效对象;引用没有空值,必须在初始化时绑定到一个有效对象上。
  • 指针是实体,而引用是别名。
  • 指针和引用的自增(++)运算符意义不同,指针是对内存地址自增,引用是对值的自增。
  •   “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小。
  • 指针与数组

  • 数组可看作指向内存首地址的指针,对于数组而言 &array == array,
  • 求sizeof()
  • 使用环境 指针多用于动态数据结构(如链表,等等)和动态内存开辟,数组多用于存储固定个数且类型统一的数据结构(如线性表等等)和隐式分配
  • 模板类和模板函数、模板元编程

  • 模板类和模板函数是C++中的泛型编程工具,允许以通用的方式定义可以适用于不同数据类型的类和函数。
  • 模板元编程(Template metaprogramming)是一种在编译期进行计算和代码生成的技术,从而使得程序在运行时获得更高的性能。
  • 解释C++中的命名空间(Namespace)概念及其作用

  • 在C++中,命名空间是一种组织代码的机制,用于防止不同代码之间的名称冲突。它提供了一种将相关的函数、类、变量等标识符分组的方式。
  • 避免名称冲突 代码组织 全局声明隔离(访问其他空间的对象使用
  • 命名空间 + 作用域解析符
  • 预处理器(Preprocessor)

  • 在C++中,预处理器(Preprocessor)是一个用于代码预处理的工具,它在编译之前对源代码进行一系列的文本替换和指令处理。预处理器指令以井号(#)开头,并且不是真正的C++代码。
  • 宏定义(Macro Definition)
  • 条件编译(Conditional Compilation)
  • 文件包含(File Inclusion)
  • 预定义宏(Predefined Macros)
  • 解释虚析构函数的作用和使用场景

  • 当基类指针指向派生类对象时,如果基类的析构函数不被声明为虚函数,那么在删除(或释放)这个指针时,只会调用基类的析构函数而不会调用派生类的析构函数。这可能导致资源泄漏或行为异常。
  • 通过将基类的析构函数声明为虚函数,可以实现动态绑定,在删除(或释放)指向派生类对象的基类指针时,会首先调用派生类的析构函数,然后再调用基类的析构函数。
  • 基类指针来操作和管理一个多态对象集合时
  • 当一个派生类拥有自己独特资源
  • C++中进行类型转换操作?

  • 隐式类型转换(Implicit Conversion):
  • 定义:编译器自动执行的类型转换操作,无需显式指定。
  • 示例:当一个表达式涉及不同类型时,编译器会根据一定的规则进行自动转换。例如,将整数赋给浮点数变量、将较小的整数类型提升为较大的整数类型等。
  • C风格强制类型转换(C-style Cast):
  • 定义:使用C语言风格的强制类型转换操作符进行显示转换。
  • 语法:(type)expression
  • 示例:int num = (int)3.14; // 将浮点数3.14强制转换为整型,并赋值给num
  • 函数风格强制类型转换(Functional Cast):
  • 定义:使用函数风格的强制类型转换操作符进行显示转换。
  • 语法:type(expression)
  • 示例:double value = double(5)/2; // 将整型5先转换为双精度浮点数,然后进行除法运算
  • dynamic_cast 运算符(Dynamic Cast Operator):
  • 定义:用于执行类层次间的安全向下转型或基类到派生类的向上转型,在运行时进行检查以确保安全性。
  • 语法:dynamic_cast<type>(expression)
  • 示例:Base* basePtr = new Derived(); // 基类指针指向派生类对象 Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 运行时进行类型检查,将基类指针转换为派生类指针
  • C风格和函数风格强制类型转换提供了显式的转换方式,但容易导致错误和潜在的未定义行为。而dynamic_cast运算符在多态继承中提供了一种安全的类型转换方式,但仅适用于具备虚函数的类层次结构。选择适当的类型转换方式取决于具体需求和安全性要求。
  • RTTI

  • RTTI(Run-Time Type Identification)是C++中的一个特性,用于在运行时获取对象的实际类型信息。
  • 识别对象的实际类型:通过RTTI,可以在运行时判断一个基类指针或引用所指向的对象的具体派生类类型。这对于处理多态性(Polymorphism)非常有用,允许程序在运行时根据实际情况进行不同操作。
  • 执行安全的向下转型(Downcasting):通过RTTI,可以将基类指针或引用转换为相应派生类指针或引用,而不会出现类型错误。这样可以避免由于类型不匹配导致的程序崩溃或未定义行为。
  • C++中的强制转型操作符

  • 在C++中,有四种类型的强制转型操作符可以用来进行类型转换。它们分别是:static_cast、dynamic_cast、reinterpret_cast和const_cast。每种操作符都有其特定的使用场景,下面对它们进行解释:
  • 1. `static_cast`: 静态转型操作符用于执行常见的隐式类型转换,如数值之间的转换、基类指针或引用到派生类指针或引用的转换等。
  • 2. dynamic_cast: 动态转型操作符主要用于安全地进行基类和派生类之间的向上或向下转型(多态性)。它会在运行时检查类型信息,如果无法完成转型,则返回空指针(对于指针)或抛出异常(对于引用)。例如:
  • 3. reinterpret_cast: 重新解释转型操作符用于执行低级别的类型转换,如指针之间的转换、将整数类型转换为指针类型等。它允许在不同类型之间进行无关的转换,并且具有较高的风险和不可移植性,因此应谨慎使用。例如:
  • 4. const_cast: 常量转型操作符用于去除表达式中的常量性(const)或易变性(volatile)。它主要用于修改对象的常量属性,但仍然需要注意不要违反程序设计原则和引发未定义行为。例如:
  •    const int num = 10;
  •    int* ptr = const_cast<int*>(&num);
  •    const MyClass obj;
  •    MyClass& ref = const_cast<MyClass&>(obj);
  • 需要注意的是,强制转型操作符应该谨慎使用,确保在合适的情况下进行合理而安全的类型转换。错误的使用可能会导致编译错误、运行时错误或未定义行为。
  • 请解释C++中的静态断言(Static Assertion)是什么,如何使用它?

  • 静态断言使用关键字 static_assert 来定义,其语法如下:
  • static_assert(condition, message);
  • 其中,condition 是一个表达式或常量,表示需要检查的条件。如果 condition 的结果为 true,则静态断言通过;如果结果为 false,则会触发编译错误,并将 message 输出作为错误信息。
  • 使用静态断言可以在编译时对代码进行一些约束和验证,例如检查类型大小、常量值等。当某个条件不满足时,编译器会提供有意义的错误信息,帮助开发者及早发现潜在问题。
  • C++中的内联函数

  • C++中的内联函数是一种特殊类型的函数,它可以通过将函数体插入到调用点来提高程序的执行效率。使用内联函数可以避免函数调用的开销,减少了函数调用和返回的时间消耗。
  • 优势:
  • 提高性能: 减少开销: 避免跳转:
  • 限制:
  • 代码膨胀:
  • 编译时间增长: 虚拟成员无法内联化: 复杂逻辑限制:
  • C++中的名字修饰规则

  • C++中的名字修饰规则,也被称为Name Mangling是一种编译器在将函数和变量名称转换为可供链接的对象文件使用的内部表示形式的过程。
  • 作用:
  • 函数重载:C++支持函数重载,即多个函数可以拥有相同的名称但具有不同的参数列表。名字修饰规则使得编译器能够根据参数类型和个数来区分不同的函数。
  • 命名空间:C++中命名空间允许我们在代码中创建逻辑上分组的命名空间。名字修饰规则确保在不同命名空间中定义了相同名称的函数或变量时,它们不会发生冲突。
  • 模板特化:C++中模板允许我们编写通用代码,以适应多种数据类型。通过名字修饰规则,编译器能够区分出模板的不同特化版本。
  • 类成员函数:类成员函数在内部存储时需要添加额外信息来标识所属类。名字修饰规则帮助编译器生成独一无二的符号来表示每个类成员函数。
  • 尾递归是指在函数的最后一步调用自身的递归形式。具体来说,尾递归函数在递归调用时不进行额外的操作或计算,而是直接返回递归调用的结果。
  • 尾递归优化是一种编译器优化技术,通过对尾递归函数进行优化,可以将其转化为等价的迭代循环结构,以减少函数调用的开销和避免栈溢出问题。
  • 实现尾递归优化

  • 将尾递归函数转换为迭代形式:将函数中需要传递给下一次递归调用的参数改为更新当前状态所需的参数。
  • 重复利用栈帧:由于不再需要保存每次递归调用的状态信息,可以复用同一个栈帧来存储变量值和执行位置。
  • 函数调用替代为跳转:使用跳转语句(例如goto)而非常规的函数调用语句,使程序在循环内部重新执行代码块而无需创建新的栈帧。
  • 通过尾递归优化,可以避免每次递归都要创建新的栈帧、保存上下文信息,并减少了内存消耗和执行时间。这对于涉及大量迭代计算的递归函数尤为重要,可以提高程序的性能和效率。
  • RAII(资源获取即初始化)在C++中的概念和应用场景。

  • RAII(Resource Acquisition Is Initialization)是C++中一种编程范式,用于管理资源的获取和释放。其核心思想是将资源的获取和初始化绑定在一起,并借助析构函数来确保资源在作用域结束时被正确释放。
  • 以下是几个应用场景:
  • 动态内存管理:使用std::unique_ptr或std::shared_ptr智能指针代替显式地调用new和delete来管理堆上分配的内存。当智能指针超出作用域时,它们会自动调用析构函数释放内存。
  • 文件处理:使用类对象封装文件操作,比如std::ifstream和std::ofstream。在构造函数中打开文件,在析构函数中关闭文件。这样可以确保文件在离开作用域前始终被正确关闭。
  • 互斥锁:使用std::lock_guard或std::unique_lock结合std::mutex来实现自动加锁和解锁操作。这样可以避免忘记手动释放互斥锁而导致死锁等问题。
  • 资源清理:利用类对象进行资源的自动清理。比如,在构造函数中创建一些资源(如数据库连接),在析构函数中释放这些资源,确保在异常等情况下也能正确释放。
  • RAII的优势是使代码更加安全、可靠,并且减少手动管理资源的负担。通过利用C++的对象生命周期和析构函数机制,可以实现高效、可维护和异常安全的代码。
  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值