【12】C++岗位求职面试八股文系列文章(语言基础)
第一篇:语言基础
第二篇:设计模式
第三篇:数据库
第四篇:计算机网络
第五篇:操作系统
第六篇:LInux
第七篇:数据结构
第八篇:智力题
[221]hashtable 扩容和如何解决冲突
1.哈希表的扩容(1)为什么要扩容使用链地址法封装哈希表时, 填装因子(loaderFactor)会大于1,理论上这种封装的哈希表时可以无限插入数据的但是但是随着数据量的增多,哈希表中的每个元素会变得越来越长, 这是效率会大大降低。 因此,需要通过扩容来提高效率。(2)如何扩容Hashtable每次扩容,容量都为原来的2倍加1,而HashMap为原来的2倍。此时,需要将所有数据项都进行修改(需要重新调用哈希函数,来获取新的位置)。 哈希表扩容是一个比较耗时的过程,但是一劳永逸。(3)什么情况下扩容常见的情况是在填装因子(loaderFactor) > 0.75是进行扩容。
如何解决哈希冲突哈希冲突:不同key值产生相同的地址,解决哈希冲突通常有开放地址法和链地址法,公共溢出区法,再散列法两种方法,分别如下:
1开放定址法即当一个关键字和另一个关键字发生冲突时,使用某种探测技术在Hash表中形成一个探测序列,然后沿着这个探测序列依次查找下去,当碰到一个空的单元时,则插入其中。比较常用的探测方法有线性探测法,
2链地址法采用数组和链表相结合的办法,将Hash地址相同的记录存储在一张线性表中,而每张表的表头的序号即为计算得到的Hash地址。hashmap就是用此方法解决冲突的
1开放定址法(线性探测再散列,二次探测再散列,伪随机探测再散列):一旦发生了冲突就去寻找下一个空的哈希地址,2再哈希法:准备若干个hash函数,如果使用第一个hash函数发生了冲突,就使用第二个hash函数,第二个也冲突,使用第三个…3链地址法4公共溢出区法:建立一个特殊存储空间,专门存放冲突的数据。此种方法适用于数据和冲突较少的情况。 在查找时,先用给定值通过哈希函数计算出相应的散列地址后,首先 首先与基本表的相应位置进行比较,如果不相等,再到溢出表中顺序查找。
Hashtable的默认容量为11,默认负载因子为0.75(HashMap默认容量为16,默认负载因子也是0.75);Hashtable的容量可以为任意整数,最小值为1,而HashMap的容量始终为2的n次方;链接
[222]说说 push_back 和 emplace_back 的区别
如果要将一个临时变量push到容器的末尾,push_back()需要先构造临时对象,再将这个对象拷贝或者移动到容器的末尾,释放原临时对象。emplace_back()则直接在容器的末尾构造对象,这样就省去了拷贝的过程。
[223]STL线程不安全的情况
在对同一个容器进行多线程的读写、写操作时;在每次调用容器的成员函数期间都要锁定该容器;在每个容器返回的迭代器(例如通过调用begin或end)的生存期之内都要锁定该容器;在每个在容器上调用的算法执行期间锁定该容器
[224]类模板分文件编写
[225]memcpy和strcpy的区别
strcpy针对字符串memcpy可以是数组、对象、字符串等,需要指定长度
[226]C++11新特性
重点:智能指针、移动语义、右值引用、类型转换
具体解析
C++新特性主要包括包含语法改进和标准库扩充两个方面,主要包括以下11点:1.语法的改进
(1)统一的初始化方法
2)成员变量默认初始化
(3) auto关键字
用于定义变量,编译器可以自动判断的类型(前提:定义一个变量时对其进行初始化),编译时候就可以推断类型,auto不能用作函数参数,在类中auto不能⽤作⾮静态成员变ᰁ量
(4) decltype 求表达式的类型
编译时期进行自动类型推导。常用于模板中。
最后一个是int类型,不带引用
带引用必须是用括号包起来的左值,要么原来就有左值返回类型后置:
(5)智能指针 shared_ptr
多个 shared_ptr 智能指针可以共同使用同一块堆内存。并且,由于该类型智能指针在实现上采用的是引用计数机制
上面倒数第二行,倒数第四行错误
第一块代码:ps转让所有权给vo,所以ps不可以再用了。运行阶段报错,第四行报错第三块代码:ps转让所有权给vo,所以ps不可以再用了。编译阶段报错,第三行报错第二块代码不转让所有权。
Auto_ptr、unique_ptr会进行所有权转让,shared_ptr不会
auto_ptr有拷贝语义,拷贝后源对象变得无效,这可能引发很严重的问题;而unique_ptr则无拷贝语义,但提供了移动语义,这样的错误不再可能发生,因为很明显必须使用std::move()进行转移(以下两个都是临时右值)
Autoptr不可以实现delete[] new[],而uniquestr可以Weekptr和sharedptr一起解决循环引用问题
Weak_ptr不影响引用记数,不改变内存值,只能访问,解决循环引用
智能指针的线程安全性添加链接描述线程安全:多线程操作一个共享数据的时候,保证所有线程的行为是符合预期的则称为线程安全。(1) 同一个shared_ptr被多个线程“读”是安全的;(2) 同一个shared_ptr被多个线程“写”是不安全的;(3) 共享引用计数的不同的shared_ptr被多个线程”写“ 是安全的但标准库实现的时候考虑到了这个问题,基于原子操作的方式保证share_ptr能够实现原子的操作引用计数。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。
异常规范
作用域内枚举
(6)空指针 nullptr(原来NULL)
引⼊nullptr关键字来区分空指针和0。原先NULL既可以是0也可以是空指针。
(7)基于范围的for循环
(8)右值引用和move语义
让程序员有意识减少进行深拷贝操作左值: 可以放在等号左边,可以取地址并由名字 字符串字⾯值"abcd"也是左值,不是右值右值: 不可以放在 i 等号左边,不能取地址,没有名字++i、--i是左值,i++、i--是右值常量左值引用既可以操作左值,也可以操作右值
左值引用需要用左值进行初始化,右值引用需要用右值进行初始化,
右值引用主要用于移动语义和完美转发,右值:字面常量、表达式、返回值的函数
move语义将某个左值强制转化为右值。基于 move() 函数特殊的功能,其常用于实现移动语义move( arg ) //其中,arg 表示指定的左值对象。该函数会返回 arg 对象的右值形式。
完美转发Forward()即使用简单的方式即可在函数模板中实现参数的完美转发完美转发,它指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
标准库扩充(往STL里新加进一些模板类,比较好用)
(9)无序容器
(哈希表) 用法和功能同map一模一样,区别在于哈希表的效率更高unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset
(10)正则表达式
可以认为正则表达式实质上是一个字符串,该字符串描述了一种特定模式的字符串
(11)Lambda表达式
[227]说说 C++ 中智能指针和指针的区别是什么?
1.智能指针如果在程序中使用new从堆(自由存储区)分配内存,等到不需要时,应使用delete将其释放。C++引用了智能指针auto_ptr,以帮助自动完成这个过程。随后的编程体验(尤其是使用STL)表明,需要有更精致的机制。基于程序员的编程体验和BOOST库提供的解决方案,C++11摒弃了auto_ptr,并新增了三种智能指针:unique_ptr、shared_ptr和weak_ptr。所有新增的智能指针都能与STL容器和移动语义协同工作。
2..指针C 语言规定所有变量在使用前必须先定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是专门用来存放地址的,所以必须将它定义为“指针类型”。
3.智能指针和普通指针的区别在于智能指针实际上是对普通指针加了一层封装机制,区别是它负责自动释放所指的对象,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命期。
[228]说说 C++中的智能指针有哪些?分别解决的问题以及区别?
1.C++中的智能指针有4种,分别为:shared_ptr、unique_ptr、weak_ptr、auto_ptr,其中auto_ptr被C++11弃用。
2.使用智能指针的原因申请的空间(即new出来的空间),在使用结束时,需要delete掉,否则会形成内存碎片。在程序运行期间,new出来的对象,在析构函数中delete掉,但是这种方法不能解决所有问题,因为有时候new发生在某个全局函数里面,该方法会给程序员造成精神负担。此时,智能指针就派上了用场。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以,智能指针的作用原理就是在函数结束时自动释放内存空间,避免了手动释放内存空间。四种指针分别解决的问题以及各自特性如下:
(2)unique_ptr(替换auto_ptr)unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露,例如,以new创建对象后因为发生异常而忘记调用delete时的情形特别有用。采用所有权模式,和上面例子一样。
(3)shared_ptr(非常好使)shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。
成员函数:use_count 返回引用计数的个数unique 返回是否是独占所有权( use_count 为 1)swap 交换两个 shared_ptr 对象(即交换所拥有的对象)reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr sp(new int(1)); sp 与 sp.get()是等价的
智能指针是个模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,一个是类,一个是指针。例如std::shared_ptr<int> p4 = new int(1);的写法是错误的
拷贝使得对象的引用计数增加1,赋值使得原对象引用计数减1,当计数为0时,自动释放内存。
(4)weak_ptrweak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr。weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作,它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
weak_ptr的构造和析构不会引起引用计数的增加或减少。
可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb; 运行结果如下,这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。
[229]手写实现智能指针类需要实现哪些函数?
一个构造函数、拷贝构造函数、析构函数、移动函数,重载-》,重载*,add_count
[230]智能指针出现循环引用怎么解决
弱指针用于专门解决shared_ptr循环引用的问题,weak_ptr不会修改引用计数,即其存在与否并不影响对象的引用计数器。循环引用就是:两个对象互相使用一个shared_ptr成员变量指向对方。弱引用并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。
[231]STL的线程安全性
STL中的容器不是线程安全的。原因在于,STL的设计初衷是将性能挖掘到极致,而一旦涉及到加锁保证线程安全,就会对性能造成巨大的影响。而且对于不同的容器,加锁方式不同,性能可能也不同。因此STL默认不是线程安全的。如果需要在多线程环境下使用,需要调用者自行保证线程安全。
lock_guard
RAII(Resource acquisition is initialization )⻛格的机制来在作⽤域块的持续时间内拥有⼀个互斥ᰁ。 创建lockguard对象时,它将尝试获取提供给它的互斥锁的所有权。当控制流离开lockguard对象的作⽤域时,lock_guard析构并释放互斥ᰁ。它的特点如下: 1. 创建即加锁,作⽤域结束⾃动析构并解锁,⽆需⼿⼯解锁 2. 不能中途解锁,必须等作⽤域结束才解锁 3. 不能复制
unique_lock是 lockguard 的升级加强版,它具有 lock_guard 的所有功能,同时⼜ 具有其他很多⽅法,使⽤起来更强灵活⽅便,能够应对更复杂的锁定需要特点:1.创建时可以不锁定(通过指定第⼆个参数为std::defer_lock),⽽在需要时再锁定2. 可以随时加锁解锁3. 作⽤域规则同 lock_grard,析构时⾃动释放锁4. 不可复制,可移动5. 条件变量ᰁ需要该类型的锁作为参数(此时必须使⽤unique_lock)
[232]类型转换运算符
1.父子类中向上转换(安全)2. 父子类中向下转换(不安全)3.基本数据类型转换
1.父子类中向上转换(安全)2. 不支持父子类中向下转换,如果产生多态,可以3不支持.基本数据类型转换
静态转换和动态转换的区别:
- 静态支持基本数据类型转换,动态不支持;
- 静态父类向子类转换不安全,动态不支持父类向子类转换,若是多态,则可以转换。
常量转换:针对指针或引用,可以转换为const,也可以转换为非const
重新解释转换:任意类型都可以转换,所以不安全
让编译器按照目标转换类型在内存中所占的比特位重新编译原始数据类型。转换前后对象的地址值是不变的,只是对象的读取内存的比例变了而已。
[233]简述 C++ 中智能指针的特点
1.C++中的智能指针有4种,分别为:shared_ptr、unique_ptr、weak_ptr、auto_ptr,其中auto_ptr被C++11弃用。
2.为什么要使用智能指针:智能指针的作用是管理一个指针,因为存在申请的空间在函数结束时忘记释放,造成内存泄漏的情况。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,自动释放资源。
3.四种指针各自特性(1)auto_ptrauto指针独占式,存在的问题是,两个智能指针同时指向一块内存,就会两次释放同一块资源,运行时报错。(2)unique_ptrunique指针规定一个智能指针独占一块内存资源。当两个智能指针同时指向一块内存,编译报错。实现原理:将拷贝构造函数和赋值拷贝构造函数申明为private或delete。不允许拷贝构造函数和赋值操作符,但是支持移动构造函数,通过std:move把一个对象指针变成右值之后可以移动给另一个unique_ptr(3)shared_ptr共享指针可以实现多个智能指针指向相同对象,该对象和其相关资源会在引用为0时被销毁释放。实现原理:有一个引用计数的指针类型变量,专门用于引用计数,使用拷贝构造函数和赋值拷贝构造函数时,引用计数加1,当引用计数为0时,释放资源。(4)weak_ptr存在一个问题,当两个shared_ptr指针相互引用时,那么这两个指针的引用计数不会下降为0,资源得不到释放。因此引入weak_ptr,weak_ptr是弱引用,weak_ptr的构造和析构不会引起引用计数的增加或减少。
[234] weak_ptr 能不能知道对象计数为 0,为什么?
能。weak_ptr是一种不控制对象生命周期的智能指针,它指向一个shared_ptr管理的对象。进行该对象管理的是那个引用的shared_ptr。weak_ptr只是提供了对管理 对象的一个访问手段。weak_ptr设计的目的只是为了配合shared_ptr而引入的一种智能指针,配合shared_ptr工作,它只可以从一个shared_ptr或者另一个weak_ptr对象构造,它的构造和析构不会引起计数的增加或减少。
[235]weak_ptr 如何解决 shared_ptr 的循环引用问题?
为了解决循环引用导致的内存泄漏,引入了弱指针weak_ptr,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。
[236]share_ptr 怎么知道跟它共享对象的指针释放了
多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数。当无shared_ptr托管该指针时,delete该指针。
[237]智能指针是怎么实现的?什么时候改变引用计数?
(1)构造函数中计数初始化为1。(2)拷贝构造函数中计数值加1。(3)赋值运算符中,左边的对象引用计数减一,右边的对象引用计数加一。(4)析构函数中引用计数减一。=(5)在赋值运算符和析构函数中,如果减一后为0,则调用delete释放对象。(6)每次创建类的新对象时,初始化指针并将引用计数置为1,如果引用计数减至0,则删除基础对象。
[238]shared_ptr 的线程安全性,原理
多线程环境下,调用不同shared_ptr实例的成员函数是不需要额外的同步手段的,即使这些shared_ptr拥有的是同样的对象。但是如果多线程访问(有写操作)同一个shared_ptr,则需要同步,否则就会有race condition 发生。也可以使用 shared_ptr overloads of atomic functions来防止race condition的发生。
多个线程同时读同一个shared_ptr对象是线程安全的,但是如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护
[239]Unique_ptr
可以处理new【】,而aoto——ptr不可以处理更重要原因是因为防止内存崩溃
[240]Shared_ptr
多个shared_ptrs共同指向一个对象
Weak_ptr
weak_ptr是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用计数的增加或减少。没有重载 * 和 -> 但可以使用lock获得一个可用的shared_ptr对象
weak_ptr的使用更为复杂一点,它可以指向shared_ptr指针指向的对象内存,却并不拥有该内存,而使用weak_ptr成员lock,则可返回其指向内存的一个share_ptr对象,且在所指对象内存已经无效时,返回指针空值nullptr。
[续]C++岗位求职面试八股文第十三篇
更多关于算法题解、软件开发面经、机器学习算法面经、各企业面试问题记录,关注Fintech砖,持续更新中。https://www.nowcoder.com/users/873777317
企业面试记录专栏https://www.nowcoder.com/creation/manager/columnDetail/0YBWnm
机器学习面经专栏https://www.nowcoder.com/creation/manager/columnDetail/j8nNy0
软件开发面经专栏https://www.nowcoder.com/creation/manager/columnDetail/0aXKaM
更多校园招聘常见面试问题(开发、算法、编程题目)参见CSDN博客:http://t.csdn.cn/V4qbH
欢迎关注、收藏、点赞后进行问题咨询及秋招建议!
#晒一晒我的offer##实习,投递多份简历没人回复怎么办##软件开发薪资爆料##2022届毕业生现状##23届找工作求助阵地#包含C++、操作系统、数据库、计算机组成、计算机网络、设计模式、操作系统、牛客网服务器项目、综合智力题等