猫头鹰面试常见题 C++软件研发


前言

记录猫头鹰面试指南,包括C++基础,数据结构与算法,计算机网络,操作系统,数据库,Linux等相关内容。

一、C++基础

1. 什么是多态

多态有静态多态和动态多态。
静态多态:通过重载和模板实现,在编译时确定。
动态多态:通过虚函数和继承实现,在运行时确定。
动态多态实现的条件,虚函数+基类指针指向子类对象。

基类指针在调用对象的成员函数(虚函数)时,就会去查对象的虚函数表,c++内部为每个含有虚函数的类都维持一个虚函数表,所有类的对象的都指向同一个虚函数表(简称虚表)。
虚表中保存的是指向实际虚函数地址的指针数组。
虚表指针是属于类的。虚表指针指向虚表。
Q:虚函数表为什么能准确的查找相应的虚函数呢?
A:子类继承基类的虚函数表的同时,会替换重写的虚函数条目,将其转换为子类自己的虚函数。

Q:虚函数,虚函数表,虚表指针之间的关系。
虚表属于类共有,虚表指针是类的对象。每个类的对象一开始都指向虚函数表。

在这里插入图片描述

2. 为什么构造函数中不能有虚函数,也不能抛出异常?

从虚函数和构造函数的作用来考虑。
虚函数的调用是要通过虚函数表来进行,而虚函数表需要在对象实例化之后才能进行调用。而构造对象过程中,对象还没有实例化,更没有为虚函数表分配内存。如果构造函数中有虚函数,就违背了先实例化后构造的准则。

构造函数中不能抛出异常,如果异常,那么析构函数就不能使用,如果之前已经申请了内存,内存便无法被释放,造成内存泄露。

4. 为什么析构函数中不能有虚函数,也不能抛出异常?

构造函数中不能抛出异常,如果异常,内存便无法被释放,造成内存泄露。

从概念上来说,析构函数是用来销毁一个对象的。先调用子类的析构函数->基类析构函数。所以在调用基类析构函数的时候,子类的析构函数认为善后工作已经完成了,这个时候如果子类析构函数中再有虚函数,就没有意义了。

5. 为什么基类析构函数中要设成虚函数?

当基类指针指向子类对象时,在删除对象时,并没有调用子类成员对象和子类自身的析构函数,而只是调用了基类对象和基类的析构函数,造成局部销毁,也会造成内存泄漏。

6. 什么时候基类析构函数不要设成虚函数?

基类指针无需要子类对象进行操作的时候。

7. strcpy,memcpy,memset的区别

8. 右值引用的方法和优点

右值引用

Q:什么是左值,什么是右值?
A:
左值:就是在内存有确定存储地址、有变量名,表达式结束依然存在的值。
右值:就是在内存没有确定存储地址、没有变量名,表达式结束就会销毁的值。

int a=10;             //非常量左值(有确定存储地址,也有变量名)
const int a1=20;      //常量左值(有确定存储地址,也有变量名)
const int a2=20;      //常量左值(有确定存储地址,也有变量名)

//非常量右值引用
int &&b1=a;            //错误,a是一个非常量左值,不可以被非常量右值引用绑定
int &&b2=a1;           //错误,a1是一个常量左值,不可以被非常量右值引用绑定
int &&b3=10;           //正确,10是一个非常量右值,可以被非常量右值引用绑定
int &&b4=a1+a2;        //错误,(a1+a2)是一个常量右值,不可以被非常量右值引用绑定

//常量右值引用
const int &&c1=a;      //错误,a是一个非常量左值,不可以被常量右值引用绑定
const int &&c2=a1;     //错误,a1是一个常量左值,不可以被常量右值引用绑定
const int &&c3=a+a1;   //正确,(a+a1)是一个非常量右值,可以被常量右值引用绑定
const int &&c4=a1+a2;  //正确,(a1+a2)是一个常量右值,不可以被常量右值引用绑定

Q:为什么要引入右值引用
A:
1,替代需要销毁对象的拷贝,提高效率。有时,需要拷贝一个对象然后将原对象销毁,引入右值引用后,就可以让新对象直接使用旧内存并且销毁原对象。
2,移动含有不能共享资源的类对象:像IO、unique_ptr这样的类包含不能被共享的资源(如:IO缓冲、指针),因此,这些类对象不能拷贝但可以移动。这种情况,需要先调用std::move将左值强制转换为右值,再进行右值引用。

Q:坐值引用和右值引用有什么区别?
(1)表示式结束后,左值引用原值仍存在,右值原值会被销毁。左值引用绑定到有确定存储空间以及变量名的对象上,表达式结束后对象依然存在;右值引用绑定到要求转换的表达式、字面常量、返回右值的表达式等临时对象上,赋值表达式结束后就对象就会被销毁。
(2)由于(1)左值引用后后仍可利用别名修改左值对象,但因为右值引用的原值已经被销毁,不能修改。

8. C++内存管理,new和malloc

https://blog.csdn.net/lingzhe7428/article/details/118414712

9. c++智能指针

二、数据结构和算法

1.快速排序思想和归并思想

排的概念是选择一个base,base当前所处位置为i,要做到比base小的都在左面,比base大的都在右面. base最终会在k位置。步遍历所在数组,从left->right的过程中遇到比base 大的与从right->left中遇到比base小的,两个数字互换.

分+治。将原来的问题分成几个子问题,再进行合并处理。比如归并排序。
将一个无序数组先分成两个,通过递归,最终得到多个长度为1的子数组,治,就是把已经排好序的两个小数组合成一个排好序的大数组,从长度为1开始,最终合成一个大数组。
常见排序

2. 哈希函数和哈希冲突

哈希

三、常用STL

Standard Template Library,标准模板库,是C++的标准库之一,一套基于模板的容器类库,还包括许多常用的算法,提高了程序开发效率和复用性。STL包含6大部件:容器、迭代器、算法、仿函数、适配器和空间配置器。

参考链接

1. vector

(1)vector底层原理
包括三个迭代器。
start-finish:已经使用范围。
start-end_of_storage:整块连续空间,包括备用部分的尾部。
注:当存储空间不够时,会申请原理的1.5~2倍空间,将原来数据拷贝到新的内存空间,接着释放原来的那片空间。
在这里插入图片描述
(2)vector中reserve和resize的区别
reserve可以减少多次开辟,释放空间的问题,是直接扩充到已经确定的大小,保证vector中的空间大小(capacity)最少达到参数所指定的n。
resize可以改变有效空间的大小(???)
(3)vector中的元素类型可以是引用吗
vector底层要求是连续的对象排列,引用并非对象,没有实际地址,因此不能是引用。
(4)vector迭代器实效的情况
vector中插入一个元素时,引起内存重新分配,指向原内存的迭代器全部实效,当删除一个元素时,迭代器也会实效,所以删除某个元素时,it=vec.erase(it);。
(5)正确释放vector的内存(clear(), swap(), shrink_to_fit())

vec.clear():清空内容,但是不释放内存。
 vector().swap(vec):清空内容,且释放内存,想得到一个全新的vector。
 vec.shrink_to_fit():请求容器降低其capacity和size匹配。
 vec.clear();vec.shrink_to_fit();:清空内容,且释放内存。

2. list

list的底层是一个双向链表。
在这里插入图片描述

3. deque

双端对了队列在这里插入图片描述

4. priority_queue

底层原理:用堆实现,堆首元素一定是当前队列中优先级最高的那个
一般和map一起用

5. map,set,multiset,multimap

(1)各自区别
map和set都是不允许重复,但multi是允许重复的
(2)map和set的插入效率要比其他容器搞,而且每次insert之后之前保存的iterator不会失效
因为存储的是接待,不需要内存的拷贝和移动。插入的操作就是指针换来换去,但是内存没有变。
(3)map和set为什么不能像vector那样有reverse函数来预分配数据?
map和set存储的是节点
(4)红黑树
map,set,multiset,multimap和epoll底层都是红黑树。

6. unordered_map、unordered_set

(1)底层原理
防冗余的哈希表(除留余数表),哈希表最大的优点就是把数据的存储和消耗之间降低,以牺牲内存为代价。
(2)unordered_map 与map的区别?使用场景?
unordered_map 需要hash函数,等于函数,map需要比较函数。
存储结构:unordered_map 采用hash存储,map使用红黑树。数据结构是不同的。
查找速度:unordered_map 会快一些,属于常数级别,但内存要求高一些。

7. 迭代器

8.线程安全问题

四、操作系统

1. 进程和线程区别

进程是资源分配的最小单位,线程是系统调度的最小单位

2.上下文切换

1,先保存当前进程的资源->放入寄存器中
2,通过调度算法获取下一个进程->使用PC指针
3,切换到下一个进程(延伸到调度算法,先到先得算法+短作业优先+时间片调转+轮询,要每个都能举一个例子和各自优缺点)
4,进程切换相当于线程切换(PC指针)+内存切换。
有可能会引出内核级线程和用户级线程,要会区别。

3.死锁

产生条件:多个进程各自占有资源,又各自需要申请资源,造成环路等待的情况。
处理:
一次性申请所有资源->对资源要求高
资源有序申请->对算法要求高
引出银行家算法例子。

4.os的内存管理

Q:如何实现虚拟内存?
分页和分段
Q:介绍分页和分段的优缺点

5.进程如何通信,各自优缺点

共享内存,管道,socket,信号量

五、计算机网络

六、Linux

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值