C++面试八股文

4.C++11的新特性:

(1)auto关键字,在编译时根据初始值自动推导类型,必须初始化,通常作为变量或返回值。

(2)decltype(declare type)用于推导数据类型,用于声明变量、配合auto设置返回值(如下:)(相较于auto不会计算初始值,可以加上括号获取引用)

template

auto processProduct(const Creator& creator) -> decltype(creator.makeObject()) {

    auto value = creator.makeObject();

    return value;

}

(3)nullptr,空指针,与NULL(即int 0)进行区分。

(4)在make_pair的基础上扩展了make_tuple,可以构造N元组。

(5)容器初始化可以直接在{}内。

(6)简化for循环,for(auto element : container)

(7)Lambda表达式,可以创建函数对象,通常为:

[可以使用的Lambda表达式之外的全局变量](参数){运算;}

例如:for_each(vector1.begin(), vector.end(), [b](int x){ cout << b+x << endl; });

sort(vector1.begin(), vector.end(), [](int a, int b){ return a>b ;})

智能指针的析构函数

5.栈溢出:栈使用的内存是有限的,在编译时就确定了。如果程序运行期间栈内存超出最大值,就会发生栈溢出。
常见的原因是:递归(每次调用函数都会将局部变量放入栈中),死循环,使用超大对象(没有new到内存上,例如存面片信息的结构体)。

6.C++11智能指针:控制对象的生命周期,自动释放空间。实现原理是引用计数。C++11提供了三种智能指针,shared_ptr(共享指针),unique_ptr(独占指针),weak_ptr(弱指针)。

(1)shared_ptr:常用的智能指针,允许多个指针指向同一个内部对象。通过“.”调用智能指针函数,包括use_count获取引用数,move转移给其他智能指针,reset重置指针(而非对象),get()获取指针;使用对象的变量和函数的方式与普通指针相同(通过指向运算符“->”)。可以通过lambda表达式设置析构函数,一般使用default_delete。

(2)unique_ptr:不允许其他指针指向其内部对象。同样支持move和reset,但是不允许赋值、拷贝操作(make_unique)。实现原理:它的本质是类,离开作用域时会调用析构函数;把拷贝构造函数和赋值运算为private,从而禁用赋值和拷贝。

(3)weak_ptr:不会增加引用数,不控制对象生命周期(存在weak_ptr也会被正常销毁),不能操作资源(不能通过weak_ptr使用对象),用于监控shared_ptr的资源。通过expired函数判断内部对象是否被释放,lock创建shared_ptr(引用值会增加),reset相同。

循环引用:两个类A和B中分别有指向对方的shared_ptr成员变量,分别创建A和B对象的shared_ptr并让他们的成员变量指向对方,这样即使reset A和B的对象指针,二者的引用计数也均不为0,造成内存泄漏。解决办法:有一方采用weak_ptr即可。

C++面试八股文_第1张图片

weak_ptr可以存在vector等C++标准容器中,因为支持拷贝构造和赋值运算。但是由于不可操作指针指向的资源,不可以存在set这种容器中。

7.static:声明静态变量、静态函数,放在静态变量存储区中,生命周期是整个程序运行期间。类的静态成员变量、静态函数是所有对象共享的;即使在函数内部声明静态变量,也会置于静态存储区;即使重复声明,依旧会使用静态存储区内已经创建的变量(即重复声明无效)。

应用场景:类的静态成员变量、静态函数是所有对象共享的。静态函数通常用于配置类(设置系统属性),工具类(例如不需要关注内部细节的数据交换)。

全局变量与静态变量的区别:全局变量通过extern声明,作用于整个项目,静态变量通过static声明,作用于当前类。

extern:声明全局变量或函数的作用范围,使用其他文件的全局变量或函数时用到。

8.进程与线程:

(1)进程:是操作系统分配资源的基本单位。

(2)线程:是进程的子单位,是CPU调度的基本单位,共享同一进程的资源。线程执行本质是函数执行,有独立的线程上下文,包括程序计数器、栈区、寄存器。

(3)区别:①进程是操作系统分配资源的基本单位,线程是CPU调度的基本单位。②线程创建、撤销开销小,创建、撤销进程时需要分配、回收资源。③线程有更好的并发性,因为一个进程中的多个线程可以并发执行。

34.死锁:两个或两个以上的进程在执行过程中,由于竞争资源而导致无法推进的现象。

死锁的4个必要条件:(1)互斥条件:一个资源只能被一个进程保持(2)请求与保持条件:一个进程请求资源而阻塞,且保持资源(3)不剥夺条件:不能剥夺进程的保持资源(4)循环等待条件:进程之间形成循环等待关系。

避免死锁:资源共享、分配所有资源后执行进程、资源被剥夺、有序分配资源。

检查死锁:银行家算法。

破坏死锁:撤销进程。

死锁的原因:(1)资源不足(2)进程执行次序不合适

9.同步与互斥:

同步:不同进程中的程序片段必须按照特定次序执行。实现访问者对资源的有序访问。

互斥:某个进程运行某个程序片段时,其他进程不能运行该程序片段。保证唯一性和排他性。

10.进程间的通信方式:

(1)共享内存,多个进程可以访问同一块内存空间,通常需要互斥锁,优点快,缺点大小受限。

(2)管道,是一块缓冲区,半双工(只允许单向传输)。

(3)消息队列,是消息的链表,支持读写信息操作。通常需要互斥信号量和同步信号量配合完成传输。

(4)信号量,整数计数器,实现互斥与同步。

(5)信号,一种异步通信机制,需要设置信号监听。

(6)Socket,支持跨网络不同主机之间的进程通信

37.线程同步:多个线程相互协调工作从而达到一致性。

4种方式:(1)互斥锁:线程在操作共享资源前会先加锁,保护了一个临界区(一段独占资源访问的代码)(2)读写锁:支持多个进程同时读,不支持同时写或边读边写(3)信号量:非负的整数计数器,控制公共资源访问,提供了PV原子操作(4)条件变量:判断资源是否可用,条件没有发生时阻塞线程,条件发生时唤醒线程。

11.C++面向对象的三大特征:封装、继承、多态。

(1)封装:把对象属性和行为隐藏在内部,只能通过接口访问。作用是使代码模块化。

(2)继承:子类继承父类的属性和行为,且可以重写父类行为。除了private属性、行为和final行为、类(不能继承)。使用场景:向下细化(几何基类),向上抽象(二次曲线)。作用是代码重用。

(3)多态:同一个接口产生不同的行为,通常是父类指针指向子类对象,子类对象有不同的函数实现。作用是接口重用。

①静态多态:编译时就可以确定接口。如:函数重载。

②动态多态:运行时才可以确定接口。如:虚函数。

C++面试八股文_第2张图片

有虚函数的类才有虚函数表,虚表是属于类的,保存虚函数指针的数组(对象中包含指向虚表的指针),虚函数指针赋值发生在编译阶段,虚表中的指针指向虚函数(子类中有自己的虚表,指向重写函数),不包含普通函数。

12.private、protected、public:

访问权限:

private:只有该类的函数和友元函数可以访问。

protected:private基础上,子类中的函数可以访问。

public:protected基础上,该类的对象可以访问。

继承:

private继承:父类的方法也变成private了

protected继承:父类的public方法也变成protected了(private不变)

public继承:啥都不变

friend:描述友元函数、类的关键字,实现类的数据共享,可以访问private成员和protected属性和行为。工具类需要频繁访问实体类的属性时,可以设置为友元类。

13.vector的clear是迭代调用保存的对象的析构函数,并不是真正地释放内存。vector的capacity不变,在vector析构函数中才会释放内存。

14.new、delete、malloc、free:(类型、操作、参数、返回值、结果)(1)new和delete是关键字,malloc和free是库函数;(2)new、delete通过构造函数和析构函数自定义操作,malloc和free不存在这类操作(3)new无需指定分配空间大小,malloc需要确定;(4)new返回类型为对象指针,malloc返回void指针(void*,可以存放任意地址)需要强制转换为对象类型的指针;(5)分配失败时,new抛出异常,malloc返回空指针。

15.返回值类型不同不是重载,编译器在编译时,不会去判断函数的返回类型。

16.空类声明时没有任何成员函数;空类定义时,编译器会为空类生成6个函数:缺省构造、拷贝构造、析构函数、取址运算符、const取值运算符、赋值运算符。

17.缓存是CPU与内存之间的容量较小但速度很快的存储器;缓冲区是用于保存两个设备之间或应用程序和设备之间所传输数据的内存区域。

区别:(1)目的:Cache是为了减少访问时间;缓存是为了平衡速度差异和传输数据大小差异。(2)存储内容:Cache中保存的是RAM中频繁使用的数据,缓冲区存储的是两个设备之间或应用程序和设备之间的传输数据。

18.内存对齐:把数据存放在字的整数倍的地址指向的内存中。

内存对齐原则:(1)数据成员对齐:从自身大小整数倍内存地址开始存储。(2)结构体成员对齐:从内部最大成员的大小的整数倍的地址开始存储。(3)结构体对齐:结构体总体大小必须是其内部最大成员的整数倍,不足的要补齐。

内存对齐原因:(1)某些硬件设备只支持对齐数据存取(2)某些硬件设备不能保证取非对齐数据是原子操作(3)非对齐数据可能引发“对齐陷阱”(4)对齐数据存取速度快

内存对齐的优点:便于不同平台移植;提高内存访问效率。

19.内存损坏:

内存泄漏:分配空间后没有释放。

内存溢出:申请空间时空间不足。

内存越界:使用了不该使用的内存。(强制转换、字符串拷贝)

20.右值引用:绑定到右值的引用,右值引用转移了资源所有权,避免了拷贝,开销很小。改变了右值的生命期(通常右值作为临时变量,拷贝之后被直接销毁)。

应用场景:(1)函数传参,减少拷贝。(2)移动语义,转移资源所有权,避免拷贝(通过移动构造函数实现)(3)完美转发,函数模板将参数转发给内部调用的其他函数。

(左值与右值的区分:能取地址则为左值,左值或不能取地址为右值。)

std::move用于将左值转换为右值引用。原理:利用引用折叠原理,右值保持不变,左值变为左值引用,然后移除引用得到具体类型,返回右值引用。

21.深拷贝:新对象和原对象占用不同的内存空间,栈和堆都拷贝。浅拷贝:新对象和原对象占用同一块内存空间,栈拷贝。一般对于指针变量采用深拷贝。

22.可调用对象:(1)函数与函数指针,std::function对象、bind创建的对象(2)仿函数:重写函数调用运算符(operator())的类的对象(3)lambda表达式(auto a = lambda)。

23.const:常量关键字,必须初始化,保存在常量存储区。(1)修饰变量表示不可更改(2)修饰指针,若在*前表示不能改内部值,若在*后表示不能该指针指向的地址。

24.set与unordered_set都是集合,一个有序,一个无序。

set/map基于红黑树实现。

红黑树:具有红黑节点的二叉树,满足以下条件:(1)根节点为黑,叶结点为空且为黑。(2)相邻父子节点不能同时为红色(3)从根到叶的路径包含了相同数量的黑色节点。操作的时间复杂度为O(logn),最大高度是2log(n+1)。相较于平衡二叉树,不需要复杂的调整操作。

unordered_set/unordered_map基于哈希表(时间复杂度O(1),通过索引查找)实现。

unordered_set自定义数据类型需要两步:(1)重载元素逻辑相等(==)运算符(2)定义哈希函数。

常用的哈希函数:(1)直接寻址(线性函数)(2)平方取中(3)除留取余(4)随机数。

哈希冲突解决方式:(1)开放定址法(2)链地址法(3)再哈希法。

25.STL中常用的数据结构:list双向链表,slist单向链表,stack栈,queue队列,vector动态数组,set集合,map映射容器。

26.typedef:为已有的类起一个新名字,有类型检测。(vector3d,point3d)

define:宏定义,无类型检测,在编译预处理阶段进行文本替换,包括类型、变量名等。

inline:内联函数,有类型检测,编译时编译器会把函数代码副本放置在每个函数执行的地方,不通过函数调用机制(跳进跳出)而是直接在调用点处展开,以空间换时间,提高运行效率。使用场景:函数体小且多次被调用(不要有递归)。

27.编译过程:预处理、编译、汇编、链接。

预处理:处理#开头文件(宏定义、头文件等)。

编译:把cpp文件翻译成汇编文件。

汇编:把汇编文件翻译成机器指令。

链接:把目标代码和库文件链接成整体。链接方式:静态链接:程序执行时链接,运行快但是占空间。动态链接:程序运行期间链接,运行慢但是省空间。

28.C++内存分区(从低到高):代码区、常量存储区、全局区/静态存储区、堆、栈。

代码区:放代码段(二进制机器指令)。

常量存储区:放常量。

全局区/静态存储区:放全局变量和静态变量。

堆:动态申请(程序员)的空间;堆是不连续的小内存块,申请时动态查找满足需求的内存块。

栈:编译器自动分配的空间,放局部变量、参数等;栈是连续的,申请时空间不够就会栈溢出。

29.指针:保存了内部对象的地址。

(1)野指针:指向不可用内存区域的指针。产生原因(1)没有初始化(2)释放空间后没有置空(3)超域(作用域)使用,栈空间已经释放,依然使用。

(2)悬空指针:指针指向的内存空间已经被释放。

31.图的存储:矩阵法(二维数组)、邻接法(链表)。BFS用队列,DFS用栈。

32.排序算法

(1)直接插入排序:比较,互换位置。On^2,O1

C++面试八股文_第3张图片

(2)归并排序:两两合一。Onlogn,On

C++面试八股文_第4张图片

C++面试八股文_第5张图片

(3)冒泡排序:相邻互换,一轮确定一个元素。On^2,O1

C++面试八股文_第6张图片

(4)快速排序:前后依次填坑。Onlogn,Ologn

C++面试八股文_第7张图片

(5)堆排序:向上调整构建最大堆,将节点放到末尾,再重新调整并构建最大堆。Onlogn,O1

C++面试八股文_第8张图片

C++面试八股文_第9张图片

33.强制类型转换:(1)static_cast用于基本数据类型的转换(2)dynamic_cast基类与派生类的指针或引用对象的转换,运行时检查(3)const_cast强制去掉常量(4)reinterpret_cast改变指针、引用的类型(与整型转换)

38.内存碎片:不可使用的内存区域,分为内部碎片和外部碎片。

内部碎片:一个进程不能完全使用分给它的内存区域。

外部碎片:未分配的连续内存区域太小。

解决方法:采用最佳适合分配、最差适合分配、段页式分配等分配方式。采用内存紧凑技术,移动作业,把小的分区拼接成大的分区。

39.虚拟内存:一种计算机内存管理技术,为每个进程提供虚拟地址空间,使得进程好像拥有了一块连续的内存。其原理是虚拟地址空间划分为多个页并与物理内存做映射。

你可能感兴趣的:(课程,c++,面试,开发语言)