• 面试整理(不断更新)


    C++:

    1、面向对象三大特性,并且具体举例说明
    封装、继承和多态。

    封装:
    定义:数据和代码捆绑在⼀起,避免外界⼲扰和不确定性访问。
    功能:把客观事物封装成抽象的类,并且类可以把⾃⼰的数据和⽅法只让可信的类或者对象操作,对不可信的进⾏信息隐藏,例如:将公共的数据或⽅法使⽤public修饰,⽽不希望被访问的数据或⽅法采⽤private修饰。

    继承:
    定义:让某种类型对象获得另⼀个类型对象的属性和⽅法。
    功能:它可以使⽤现有类的所有功能,并在⽆需重新编写原来的类的情况下对这些功能进⾏扩展。
    常⻅的继承有三种⽅式:
    1、实现继承:指使⽤基类的属性和⽅法⽽⽆需额外编码的能⼒
    2、接⼝继承:指仅使⽤属性和⽅法的名称、但是⼦类必须提供实现的能⼒
    3、可视继承:指⼦窗体(类)使⽤基窗体(类)的外观和实现代码的能⼒
    例如:
    将⼈定义为⼀个抽象类,拥有姓名、性别、年龄等公共属性,吃饭、睡觉等公共⽅法,在定义⼀个具体的⼈时,就可以继承这个抽象类,既保留了公共属性和⽅法,也可以在此基础上扩展跳舞、唱歌等特有⽅法。

    多态:
    定义:
    同⼀事物表现出不同事物的能⼒,即向不同对象发送同⼀消息,不同的对象在接收时会产⽣不同的⾏为(重载实现编译时多态,虚函数实现运⾏时多态)。
    功能:
    多态性是允许你将⽗对象设置成为和⼀个或更多的他的⼦对象相等的技术,赋值之后,⽗对象就可以根据当前赋值给它的⼦对象的特性以不同的⽅式运作。
    简单⼀句话:允许将⼦类类型的指针赋值给⽗类类型的指针。
    例如:
    基类是⼀个抽象对象–⼈,那学⽣、运动员也是⼈,⽽使⽤这个抽象对象既可以表示学⽣、也可以表示运动员。

    2、多态如何实现
    实现多态有两种⽅式:

    1. 覆盖(override): 是指⼦类重新定义⽗类的虚函数的做法。
    2. 重载(overload): 是指允许存在多个同名函数,⽽这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

    3、堆和栈的区别
    在C++中,内存分为5个区:堆、栈、自由存储区、全局/静态存储区、常量存储区

    栈:是由编译器在需要时自动分配,不需要时自动清除的变量存储区。通常存放局部变量、函数参数等。
    堆:是由new分配的内存块,由程序员释放(编译器不需要操作),一般一个new与一个delete对应,一个new[]与一个delete[]对应。如果程序员没有释放掉,资源将由操作系统在程序结束后自动回收。
    自由存储区:是由malloc等分配的内存块,和堆十分相似,用free来释放。
    全局/静态存储区:全局变量和静态变量被分配到同一块内存中(在C语言中,全局变量又分为初始化的和未初始化的,C++中没有这一区分)。
    常量存储区:这是一块特殊存储区,里边存放常量,不允许修改。

    堆和栈的区别:
    在这里插入图片描述
    4、 什么是对齐,以及为什么要对齐:
    4.1 概念
    现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

    4.2 对齐的作用及原因:
    各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况, 但是最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32位)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低 字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。

    4.3 对齐的原则
    有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始址%N=0"。
    数据结构中的数据变量都是按照定义的顺序来排放的,第⼀个数据变量的起始地址就是数据结构的起始地址。
    结构体本身也要根据⾃身的有效对齐值取整(结构体成员变量占用总长度,要满足是结构体有效对齐值的整数倍)
    总结:
    各变量要对齐 + 结构体整体也要对齐。

    5、指针和引用的区别

    1. 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名。
    2. 指针可以有多级,引用只有一级。
    3. 指针可以为空,引用不能为NULL且在定义时必须初始化。
    4. 指针在初始化后可以改变指向,而引用在初始化之后不可再改变。
    5. sizeof指针得到的是本指针的大小,sizeof引用得到的是引用所指向变量的大小。
    6. 当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但不是同一个变量,在函数中改变这个变量的指向不影响实参,而引用却可以。
    7. 引用本质是一个指针,同样会占4字节内存;指针是具体变量,需要占用存储空间(具体情况还要具体分析)。
    8. 引用在声明时必须初始化为另一变量,一旦出现必须为typename refname &varname形式;指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量。
    9. 引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用);指针变量可以重新指向别的变量。不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针。

    6、C++中struct和class的区别
    相同点:

    1. 两者都拥有成员函数、公有和私有部分
    2. 任何可以使用class完成的工作,同样可以使用struct完成

    不同点:

    1. 两者中如果不对成员不指定公有私有,struct默认是公有的,class则默认是私有的
    2. class默认是private继承, 而struct默认是public继承

    引申:C++和C的struct区别

    1. C语言中:struct是用户自定义数据类型(UDT);C++中struct是抽象数据类型(ADT),支持成员函数的定义,(C++中的struct能继承,能实现多态)。
    2. C中struct是没有权限的设置的,且struct中只能是一些变量的集合体,可以封装数据却不可以隐藏数据,而且成员不可以是函数
    3. C++中,struct增加了访问权限,且可以和类一样有成员函数,成员默认访问说明符为public(为了与C兼容)
    4. struct作为类的一种特例是用来自定义数据结构的。一个结构标记声明后,在C中必须在结构标记前加上struct,才能做结构类型名(除:typedef struct class{};);C++中结构体标记(结构体名)可以直接作为结构体类型名使用,此外结构体struct在C++中被当作类的一种特例。

    计算机网络:

    1、OSI七层模型和四层模型,四层模型每一层的具体功能。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2、TCP与UDP的区别

    1. 连接
      TCP是⾯向连接的,在传输前需要三次握⼿建⽴连接,UDP不需要连接,即刻传输数据。
    2. 服务形式
      TCP只能⼀对⼀,点对点服务,UDP⽀持⼀对⼀、⼀对多、多对多通信。
    3. 可靠性
      TCP保证数据可靠交付,拥有确认应答和重传机制,⽆重复、不丢失、按序到达;UDP尽可能交付,不保证可靠性。
    4. 连接控制机制
      TCP拥有流量控制、拥塞控制,保证传输安全性等,UDP在⽹络拥堵情况下不会降低发送速率。
    5. ⾸部⼤⼩
      TCP⾸部⻓度不适⽤选项字段是20字节,使⽤选项字段⻓度增加(可变),UDP⾸部固定8字节。
    6. 传输⽅式
      TCP基于字节流,没有边界,但是保证传输顺序和可靠性;UDP继承了IP层特性,基于数据包,有边界可能出现乱序和丢包。
    7. 分⽚⽅式
      TCP数据⼤于MSS时会在TCP层将数据进⾏分⽚传输,到达⽬的地后同样在传输层进⾏合并,如果有某个⽚丢失则只需要重传丢失的分⽚即可;UDP数据⼤于MTU时会在IP层分⽚,同样也在⽬的IP层合并,如果某个IP分⽚丢失,则需要将所有分⽚都进⾏重传,开销⼤。
      3、TCP如何保证安全传输
      TCP通过三次握手、四次挥手,而且TCP有流量控制和拥塞控制机制,通过这些来保证数据的安全传输。

    操作系统:
    1、

    Linux:
    1、

    设计模式
    1、单例模式
    确保⼀个类只有⼀个实例,并提供该实例的全局访问点。
    优点:
    有些实例,全局只需要⼀个就够了,使⽤单例模式就可以避免⼀个全局使⽤的类,频繁的创建与销毁,耗费系统资源。
    设计要素:
    ⼀个私有构造函数(确保只能单例类⾃⼰创建实例)
    ⼀个私有静态变ᰁ(确保只有⼀个实例)
    ⼀个公有静态函数(给使⽤者提供调⽤⽅法)
    简单来说就是,单例类的构造⽅不让其他⼈修改和使⽤;并且单例类⾃⼰只创建⼀个实例,这个实例,其他⼈也⽆法修改和直接使⽤;然后单例类提供⼀个调⽤⽅法,想⽤这个实例,只能调⽤。这样就确保了全局只创建了⼀个实例。
    单例模型6种实现及各实现的优缺点
    1、懒汉式(线程不安全)
    说明:
    先不创建实例,当第⼀次被调⽤时,在创建实例,所以被称为懒汉式。
    优点:
    延迟了实例化,如果不需要使⽤该类,就不会被实例化,节约了系统资源。
    缺点:
    线程不安全,多线程环境下,如果多个线程同时进⼊了 if (uniqueInstance == null) ,若此时还
    未实例化,也就是uniqueInstance == null,那么就会有多个线程执⾏ uniqueInstance = new
    Singleton(); ,就会实例化多个实例;
    2、饿汉式(线程安全)
    说明:
    先不管需不需要使⽤这个实例,直接先实例化好实例(饿死⻤⼀样,所以称为饿汉式),然后
    当需要使⽤的时候,直接调⽅法就可以使⽤了
    优点:
    提起实例化好了⼀个实例,避免了线程不安全问题的出现,
    缺点:
    直接实例化了实例,不再延迟实例化;若系统没有使⽤这个实例,或者系统运⾏很久之后才需
    要使⽤这个实例,都会使操作系统的资源浪费。
    3、懒汉式(线程安全)
    说明:
    实现和线程不安全的懒汉式 ⼏乎⼀样,唯⼀不同的点是,在get⽅法上 加了⼀把锁。如此⼀
    来,多个线程访问,每次只有拿到锁的的线程能够进⼊该⽅法,避免了多线程不安全问题的出
    现。
    优点:
    延迟实例化,节约了资源,并且是线程安全的。
    缺点:
    虽然解决了线程安全问题,但是性能降低了。因为,即使实例已经实例化了,既后续不会再出
    现线程安全问题了,但是锁还在,每次还是只能拿到锁的线程进⼊该⽅***使线程阻塞,等待时
    间过⻓。
    4、双重检查锁实现(线程安全)
    说明:
    双᯿检查数相当于是改进了线程安全的懒汉式。线程安全的懒汉式的缺点是性能降低了,造成
    的原因是因为即使实例已经实例化,依然每次都会有锁。
    ⽽现在,我们将锁的位置变了,并且多加了⼀个检查。也就是,先判断实例是否已经存在,若
    已经存在了,则不会执⾏判断⽅法内的有锁⽅法了。 ⽽如果,还没有实例化的时候,多个线
    程进去了,也没有事,因为⾥⾯的⽅法有锁,只会让⼀个线程进⼊最内层⽅法并实例化实例。
    如此⼀来,最多最多,也就是第⼀次实例化的时候,会有线程阻塞的情况,后续便不会再有线
    程阻塞的问题。
    为什么使⽤ volatile 关键字修饰了 uniqueInstance 实例变ᰁ?
    uniqueInstance = new Singleton();
    这段代码执⾏时分为三步:

    1. 为 uniqueInstance 分配内存空间
    2. 初始化 uniqueInstance
    3. 将 uniqueInstance 指向分配的内存地址
      正常的执⾏顺序当然是 1>2>3 ,但是由于 JVM 具有指令᯿排的特性,执⾏顺序有可能变成
      1>3>2。
      单线程环境时,指令᯿排并没有什么问题;多线程环境时,会导致有些线程可能会获取到还没
      初始化的实例。
      例如:线程A 只执⾏了 1 和 3 ,此时线程B来调⽤ getUniqueInstance(),发现
      uniqueInstance 不为空,便获取 uniqueInstance 实例,但是其实此时的 uniqueInstance 还没
      有初始化。
      解决办法就是加⼀个 volatile 关键字修饰 uniqueInstance ,volatile 会禁⽌ JVM 的指令᯿排,
      就可以保证多线程环境下的安全运⾏。
      优点:
      延迟实例化,节约了资源;线程安全;并且相对于线程安全的懒汉式,性能提⾼了。
      缺点:
      volatile 关键字,对性能也有⼀些影响。
      5、静态内部类实现(线程安全)
      说明:
      ⾸先,当外部类 Singleton 被加载时,静态内部类 SingletonHolder 并没有被加载进内存。当
      调⽤ getUniqueInstance() ⽅法时,会运⾏ return SingletonHolder.INSTANCE;
      触发了 SingletonHolder.INSTANCE ,此时静态内部类 SingletonHolder 才会被加载进内存,
      并且初始化 INSTANCE 实例,⽽且 JVM 会确保 INSTANCE 只被实例化⼀次。
      优点:
      延迟实例化,节约了资源,且线程安全,性能也提⾼了。
      6、枚举类实现(线程安全)
      说明:
      默认枚举实例的创建就是线程安全的,且在任何情况下都是单例。
      优点:
      写法简单,线程安全,天然防⽌反射和反序列化调⽤。
      防⽌反序列化
      序列化:
      把java对象转换为字节序列的过程;
      反序列化:
      通过这些字节序列在内存中新建java对象的过程;
      说明:
      反序列化将⼀个单例实例对象写到磁盘再读回来,从⽽获得了⼀个新的实例。
      我们要防⽌反序列化,避免得到多个实例,枚举类天然防⽌反序列化。
      其他单例模式 可以通过 ᯿写 readResolve() ⽅法,从⽽防⽌反序列化,使实例唯⼀᯿写
      单例模式的应⽤场景
      应⽤场景举例:
    4. ⽹站计数器
    5. 应⽤程序的⽇志应⽤
    6. Web项⽬中的配置对象的读取
    7. 数据库连接池
    8. 多线程池

      使⽤场景总结:
      (1)频繁实例化然后⼜销毁的对象,使⽤单例模式可以提⾼性能
      (2)经常使⽤的对象,但实例化时耗费时间或者资源多,如数据库连接池,使⽤单例模式,
      可以提⾼性能,降低资源损坏
      (3)使⽤线程池之类的控制资源时,使⽤单例模式,可以⽅便资源之间的通信

    项目问题:

  • 相关阅读:
    css 隐藏元素的方法
    vision transformer 详解
    tidb-cdc同步到kafka报错cdc报错CDC:ErrKafkaNewSaramaProducer
    归并排序的非递归实现
    Centos7安装RabbitMQ
    采购自动化如何帮助采购转型?
    MySQL数据库————初识数据库
    我想要一个勋章
    题目 1096:扫雷舰
    【Golang星辰图】Go语言之光照耀数据科学:揭开强大库的神秘面纱
  • 原文地址:https://blog.csdn.net/qq_27538633/article/details/126344698