百度一面面经(2021-07-16)

这篇博客汇总了C++面试中的关键知识点,包括继承(公有、私有、保护及虚继承的特性与优缺点)、多态(静态与动态多态的实现及优缺点)、封装的原理与作用,以及函数重载、重写和重定义的区别。此外,还讨论了类与结构体的差异,STL的组成部分(容器、迭代器和算法)以及vector的实现。最后,博主分享了关于链表操作的问题,如链表相交、环形链表判断,以及深拷贝和浅拷贝的概念。
摘要由CSDN通过智能技术生成

在这里说明一下,整理的面试经历的来源是牛客网,不是我本人的面试经历~

我做这个整理的目的,一是为了自己学习和巩固知识点,再一个是可以让找C++相关工作的朋友,快速过一下知识点。

整理的内容可能出现错误,欢迎指出!

面经来源:https://www.nowcoder.com/discuss/684096?source_id=discuss_experience_nctrack&channel=-1
(侵删)
在这里插入图片描述

自我介绍

项目

C++有哪些特性?
继承、多态、封装
1 继承
被继承的是父类(基类),继承出来的类是子类(派生类),子类拥有父类的所有特性。
继承方式有公有继承、私有继承、保护继承。默认是私有继承。

公有继承class A:public Base
父类中的公有成员在子类中还是公有成员,保护还是保护,子类不可以访问父类的私有成员。
私有继承class A:private Base
父类中的公有成员和保护成员在子类中变为私有成员,子类不可以访问父类的私有成员。
保护继承class A:private Base
父类中的公有成员和保护成员在子类中变为保护成员,子类不可以访问父类的私有成员。

虚继承:为了解决多重继承中的二义性问题,它维护了一张虚基类表。

优点:继承减少了重复的代码、继承是多态的前提、继承增加了类的耦合性
缺点:继承在编译时刻就定义了,无法在运行时刻改变父类继承的实现;父类通常至少定义了子类的部分行为,父类的改变都可能影响子类的行为;如果继承下来的子类不适合解决新问题,父类必须重写或替换,那么这种依赖关系就限制了灵活性,最终限制了复用性。

2 多态
多态性是指对不同类的对象发出相同的消息会有不同的实现。
C++有两种多态,称为运行时多态和编译器多态,静多态主要是通过模板和函数重载来实现,而动多态是通过虚函数来实现的。即在基类中存在虚函数,子类通过重写这些函数,使用基类的指针或者引用指向子类的对象,就可以调用子类对应的函数,动多态的函数调用机制是执行器期才能确定的,所以它是动态的。

优点:大大提高了代码的可复用性;提高了代码的可维护性,可扩充性;
缺点:易读性比较不好,调试比较困难;模板只能定义在头文件中,当工程大了以后,编译时间十分长。

3 封装
隐藏类的属性和实现细节,仅仅对外提供接口,封装性实际上是由编译器去识别关键字public、private和protected来实现的,体现在类的成员可以有公有成员、私有成员和保护成员。私有成员是在封装体内被隐藏的部分,只有类体内说明的函数,即类的成员函数才可以访问私有成员,而类体外的函数是不能访问的,公有成员是封装体与外界的一个接口,类体外的函数可以访问公有成员,保护成员是只有该类的成员函数和该类的派生类才可以访问的。

优点:隔离变化;便于使用;提高重用性;提高安全性
缺点:如果封装太多,影响效率;使用者不能知道代码具体实现。

请你介绍下多态
1 静态多态
静态多态就是编译器在编译期完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数可以调用就调用,没有的话就会发出警告或者报错。

2 动态多态
静态多态的反义词,它是在程序运行时,通过基类的指针或者引用指向子类的对象,来确定具体该调用哪一个类的虚函数。

动态多态的条件:
1基类中必须包含虚函数,并且派生类中一定要对基类中的虚函数进行重写。
2通过基类对象的指针或引用指向子类对象调用虚函数。

多态的缺陷:
1 降低了程序运行效率,多态需要去找虚表的地址
2 空间浪费

虚函数表:
虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。
虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表。
虚函数是通过一张虚函数表来实现的。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题。在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所该调用的函数。
C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置,这是为了保证取到虚函数表有最高的性能。

一般继承(无虚函数覆盖):虚函数按照其声明顺序放于表中;父类的虚函数在子类的虚函数前面。
一般继承(有虚函数覆盖):覆盖的函数会被放到虚表中原来父类虚函数的位置;没有被覆盖的函数依旧。
多重继承(无虚函数覆盖):每个父类都有自己的虚表;子类的成员函数被放到了第一个父类的表中。

多重继承(有虚函数覆盖):在子类中被重写的函数替换对应的原来父类虚函数的位置;子类的其他成员函数被放到第一个父类的表中。

哪些函数不能定义为虚函数?
1 友元函数,它不是类的成员函数
2 全局函数
3 静态函数
4 构造函数

泛型技术:说白了就是试图使用不变的代码来实现可变的算法。

虚基类的析构函数需要注意些什么?
有一个类A,A中有一个成员i,B和C都继承了A,D又同时继承了B和C,那么D中的i是来自于B还是来自于C呢?显然D包含了类A的两个拷贝,不仅多占用内存,而且还造成多个拷贝的数据不一致。
这时候就要用到虚基类(虚继承),此时B和C继承的是同一个对象A,而不是两个A,这样A就来自于同一个对象。
我猜这一题应该问的是基类的析构函数需要注意些什么:基类的析构函数需要加virtual关键字,也就是声明为虚析构函数,这样是为了子类在调用析构函数的时候调用的是子类的析构函数而非父类的析构函数,以确保资源的正确释放。否则可能会造成内存泄漏,也就是子类的部分资源没有被释放掉。

介绍下函数重载重写覆盖重定义
重载:作用域相同,指函数名相同,形参参数类型或参数个数不同的这一类函数。与返回类型无关。
重写(覆盖):作用域不同,在不同的类中;函数名,参数,返回值均相同,父类中用virtual修饰的函数在子类中重新定义。
重定义(同名隐藏):作用域不同,在不同的类中;要求同名;参数不同,无论有无virtual都是同名隐藏;参数相同,无virtual才发生同名隐藏。

类和结构体有哪些区别?
1 结构体是一种值类型,而类是引用类型。值类型用于存储数据的值,引用类型用于存储对实际数据的引用。那么结构体就是当成值来使用的,类则通过引用来对实际数据操作。
2 结构体使用栈存储,而类使用堆存储。
栈的空间相对较小,但是存储在栈中的数据访问效率相对较高。
堆的空间相对较大,但是存储在堆中的数据访问效率相对较低。
3 类是反映现实事物的一种抽象,而结构体的作用只是包含了具体不同类别数据的一种包装,结构体不具备类的继承多态特性。
4 结构体赋值是直接赋值的值,而对象赋值的是对象的地址

介绍类的三个保护等级
Public:在类体内和类体外都可以被访问。
Private:私有的,只有在类体内才可以被访问,类体外无权访问私有成员。派生类也无法访问私有成员。
Protected:保护的,只有在类体内才可以被访问,类体外无权访问私有成员。但是与private不同的是,派生类可以访问父类的protected成员。

介绍一下STL,说一下STL如何实现vector
STL,即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库中,包含容器、算法、迭代器组件。
容器:可以把它理解为存放数据的地方,常用的一些容器有:动态数组(vector)、链表(list)、栈(stack)、队列(queue)、映射(map)
迭代器:可以把它理解为指针类型
算法:它们通常需要与容器和游标配合使用,使用它们,你可以方便地对容器中的数据进行各种常见的操作,如排序操作,寻找最大元素的操作等。

Vector内部使用动态数组的方式实现的。如果动态数组的内存不够用,就要动态的重新分配,一般是当前大小的两倍,然后把原数组的内容拷贝过去。所以,在一般情况下,其访问速度和一般数组相同,只有重新分配发生时,其性能才会下降。
注意vector的size()和capacity()是不同的,前者表示数组中元素个数,后者表示数组有多大的容量。

数组和链表的区别
数组:就是相同数据类型的元素按照一定顺序排列的集合;数组的存储空间是连续的。
链表:链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素。
区别:
1 链表是链式的存储结构,不需要连续的内存空间;数组是顺序的存储结构,它的存储空间是连续的。所以链表的内存利用率高,不会浪费内存。
2 链表是通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。
3 链表的插入删除操作比数组简单,不需要移动元素,只要找到相应的位置对其进行相应的操作即可,但是寻找某个元素较为困难,需要从头结点开始遍历。
数组则是寻找某个元素较为简单,因为可以通过索引的方式找到某个元素。但是插入和删除操作较为麻烦,需要进行元素的移动。
4链表的空间大小不固定,可以动态拓展;而数组的空间大小固定,动态拓展需要开辟新的内存空间并且进行数据搬运。

仿函数的作用
它既能像普通函数一样传入给定数量的参数(形参),还能存储或者处理更多我们需要的有用信息(成员变量、成员函数)
仿函数的本质是类而非函数,它内部对操作符()进行重载,使类的功能看起来更像是一个函数。
Bool operator()(形参){…}

深拷贝和浅拷贝
浅拷贝:位拷贝、拷贝构造函数、赋值重载;多个对象共用同一块资源,同一块资源,同一块资源就会被释放多次,造成崩溃或者内存泄漏。
深拷贝:每个对象共同拥有自己的资源,必须显示提供拷贝构造函数和赋值运算符。

两个链表如何判断是否相交,以及判断交点
力扣-面试题02.07.链表相交
在这里插入图片描述
一个链表如何判断是否有环
力扣-141.环型链表
在这里插入图片描述
手撕代码:旋转链表,输出链表向右旋转k位的结果
力扣-61旋转链表,但是这一题是链表向左旋转k次
在这里插入图片描述
口述思路:一串数组,两个元素出现一次其余出现两次,获取两个出现一次的元素
力扣-136.只出现一次的数字

解题思路:用哈希映射 map 来做

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res;
    map<int,int>m;
    for(auto v:nums){
        m[v]++;
    }

    for(auto v:nums){
        if(m[v]==1){
            res=v;
            break;
        }
    }

    return res;
    }
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值