2022年C++面试题【常问重点问题】
- `1、请你说说 GET 和 POST 的区别?`
- `2、简述一下 C++ 中的多态?`
- `3、说一说进程有多少种状态,如何转换?`
- `3、请你说说指针和引用的区别`
- `4、简述一下虚函数的实现原理`
- `5、说一说 vector 和 list 的区别,分别适用于什么场景?`
- `6、什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程`
- `7、请你说说 C++ Lambda 表达式用法及实现原理`
- `8、请你说说 innodb 和 myisam 的区别`
- `9、请你说说数据库的索引是什么结构,为什么不用哈希表?`
- `10、虚析构函数有什么作用?`
- `11、说一说常用的 Linux 命令?(常见)`
- `12、简述一下堆和栈的区别`
- `13、请你说说重载,复写,隐藏的区别`
- `14、请你说说各数据类型 sizeof 是多少,sizeof 指针是多少,sizeof 原理`
- `15、说说 const 和 define 的区别`
- `16、请你说说 Redis 如何与数据库保持双写一致性`
- `17、请你说说C++引用的概念`
- `18、请你说说迭代器失效原因,有哪些情况`
- `19、请你说说动态库静态库的区别和优缺点`
- `20、请你说说虚函数可以是内联函数吗`
🏠个人主页:@编程ID
🧑个人简介:大家好,我是编程ID,一个想要与大家共同进步的程序员儿🧑如果各位哥哥姐姐在准备面试,找工作,刷算法,前后端编程题选择题都有,可以使用我找工作前用的刷题神器哦!刷题神器点这里哟🎁检测自己的学习程度,提升自己都不错呢!
💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,望能帮到各位想要找工作或者提高自己的小伙伴儿们,如果有什么需要改进的地方,还请大佬不吝赐教🤞🤞
⭐️相信大家也知道C++近年来的热度一直稳居前列,今天分享一篇C++的面试题,喜欢的宝子们点赞👍收藏 🌟哦!
1、请你说说 GET 和 POST 的区别?
简述:post采用隐式传参,get采用显示传参。get传输数据有长度的现实,而post没有长度的限制。get常用来获取数据,post常用来提交数据。get请求会被浏览器主动缓存,post请求不会被浏览器主动缓存,可手动设置缓存。get请求只支持url编码,post请求支持多种编码方式(img,text,application等等)
得分点 用法不一样、参数显隐式、参数长度。 标准回答 get主要用来获取数据,而post是提交或修改数据。get有长度限制(2048字节)而post没有。get的参数是显式的,而post是隐式的。加分回答 - get主要用来获取数据,post主要用来提交数据。 - get的参数有长度限制,最长2048字节,而post没有限制。 - get的参数会附加在url之 ,以 " ? "分割url和传输数据,多个参数用 "&"连接,而post会把参数放在http请求体中。 - get是明文传输,可以直接通过url看到参数信息,post是放在请求体中,除非用工具才能看到。 - get请求会保存在浏览器历史记录中,也可以保存在web服务器日志中。 - get在浏览器回退时是无害的,而post会再次提交请求。 - get请求会被浏览器主动缓存,而post不会,除非手动设置。 - get请求只能进行url编码,而post支持多种编码方式。 - get请求的参数数据类型只接受ASCII字符,而post没有限制。 |
---|
2、简述一下 C++ 中的多态?
简述:多态分为静态多态和动态多态。静态多态是在编译期间完成的,编译器会根据实参类型选择合适的函数调用,如果存在就调用,不存在就报错。静态多态有函数重载,运算符重载,泛型编程等。动态多态是根据基类指针或引用指向的对象,来确定调用哪个函数的过程,比如父类指针指向父类对象,则调用父类对象的虚函数;父类指针指向子类对象,则调用子类对象中的虚函数。动态多态的条件,要有继承关系,函数重写,父类指针指向子类对象。动态多态的原理是,当对象中声明了虚函数,就会在类中生成一个虚函数表,这个是有编译器自动生成和维护的。虚函数表中存放虚函数指针,每个对象都会有一个虚指针,虚指针会根据这个对象在对应类中的虚函数表里查找被调用的函数,进行调用。
得分点 静态多态、动态多态、多态的实现原理、虚函数、虚函数表 标准回答 在现实生活中,多态是同一个事物在不同场景下的多种形态。在面向对象中,多态是指通过基类的指针或者引用,在运行时动态调用实际绑定对象函数的行为,与之相对应的编译时绑定函数称为静态绑定。所以多态分为静态多态和动态多态。 1. 静态多态 静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数就调用,没有的话就会发出警告或者报错。静态多态有函数重载、运算符重载、泛型编程等。 2. 动态多态 动态多态是在程序运行时根据基类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。当父类指针(引用)指向 父类对象时,就调用父类中定义的虚函数;即当父类指针(引用)指向 子类对象时,就调用子类中定义的虚函数。 加分回答 1. 动态多态行为的表现效果为:同样的调用语句在实际运行时有多种不同的表现形态。 2. 实现动态多态的条件: - 要有继承关系 - 要有虚函数重写(被 virtual 声明的函数叫虚函数) - 要有父类指针(父类引用)指向子类对象 3. 动态多态的实现原理 当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个存储类虚函数指针的数据结构, 虚函数表是由编译器自动生成与维护的。virtual 成员函数会被编译器放入虚函数表中,存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr 指针)。在多态调用时, vptr 指针就会根据这个对象在对应类的虚函数表中查找被调用的函数,从而找到函数的入口地址。 |
---|
3、说一说进程有多少种状态,如何转换?
得分点
创建、就绪、执行、阻塞、终止
标准回答
- 进程有五种状态:创建、就绪、执行、阻塞、终止:
- 创建:一个进程启动,首先进入创建状态,需要获取系统资源创建进程管理块(PCB:Process Control Block)完成资源分配。
- 就绪状态:在创建状态完成之后,进程已经准备好,处于就绪状态,但是还未获得处理器资源,无法运行。
- 运行状态:获取处理器资源,被系统调度,当具有时间片开始进入运行状态。如果进程的时间片用完了就进入就绪状态。
- 阻塞状态:在运行状态期间,如果进行了阻塞的操作,此时进程暂时无法操作就进入到了阻塞状态,在这些操作完成后就进入就绪状态。等待再次获取处理器资源,被系统调度,当具有时间片就进入运行状态。
- 终止状态:进程结束或者被系统终止,进入终止状态。
3、请你说说指针和引用的区别
标准回答
指针和引用的区别有:
-
- 定义和性质不同。指针是一种数据类型,用于保存地址类型的数据,而引用可以看成是变量的别名。指针定义格式为:数据类型 *;而引用的定义格式为:数据类型 &;
-
- 引用不可以为空,当被创建的时候必须初始化,而指针变量可以是空值,在任何时候初始化;
-
- 指针可以有多级,但引用只能是一级;
-
- 引用使用时无需解引用(*),指针需要解引用;
-
- 指针变量的值可以是 NULL,而引用的值不可以为 NULL;
-
- 指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了;
-
- sizeof 引用得到的是所指向的变量(对象)的大小,而 sizeof 指针得到的是指针变量本身的大小;
-
- 指针作为函数参数传递时传递的是指针变量的值,而引用作为函数参数传递时传递的是实参本身,而不是拷贝副本;
-
- 指针和引用进行++运算意义不一样。
4、简述一下虚函数的实现原理
简述:
虚函数的作用:主要是实现了动态多态的机制。用父类型的指针指向其子类的实例,然后通过父类指针调用实际子类的成员函数。
实现原理:编译器在处理虚函数时,给每个对象添加一个虚函数指针,指向虚函数表。虚函数表中存储的是类中虚函数的地址。如果派生类重写了基类的虚函数,则派生类对象的虚函数表中保存的是派生类的虚函数地址,如果没有重写,则派生类对象的虚函数表中保存的是父类的虚函数地址。
消耗成本:使用虚函数时,对于内存和执行速度方面有一定的成本消耗。
1.每个对象都会变大,变大的量为存储虚函数表指针;
2.对于每个类,编译器都会创建一个虚函数表;
3.每次调用虚函数,都要有查表操作,增加了时间开销
得分点 多态、虚函数表、虚函数表指针 标准回答 1. 虚函数的作用 C++ 中的虚函数的作用主要是实现了动态多态的机制。动态多态,简单的说就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。 2. 虚函数实现原理 编译器处理虚函数时,给每个对象添加一个隐藏的成员。隐藏的成员是一个指针类型的数据,指向的是函数地址数组,这个数组被称为虚函数表(virtual function table,vtbl)。虚函数表中存储的是类中的虚函数的地址。如果派生类重写了基类中的虚函数,则派生类对象的虚函数表中保存的是派生类的虚函数地址,如果派生类没有重写基类中的虚函数,则派生类对象的虚函数表中保存的是父类的虚函数地址。 加分回答 使用虚函数时,对于内存和执行速度方面会有一定的成本:1. 每个对象都会变大,变大的量为存储虚函数表指针; 2. 对于每个类,编译器都会创建一个虚函数表; 3. 对于每次调用虚函数,都需要额外执行一个操作,就是到表中查找虚函数地址。` |
---|
5、说一说 vector 和 list 的区别,分别适用于什么场景?
解题思路
得分点
低层数据结构、内存顺序、是否支持随机访问
标准回答
-
- 区别 - vector 底层实现是数组,list 是双向链表 - vector 支持随机访问,list 不支持 - vector 是顺序内存,list 不是 - vector 在中间节点进行插入删除会导致内存拷贝,list 不会 - vector 一次性分配好内存,不够时才进行扩容,list 每次插入新节点都会进行内存申请 - vector 随机访问性能好,插入删除性能差,list 随机访问性能差,插入删除性能好
-
- 适用场景 - vecto r拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用 vector。 - list 拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。
6、什么是孤儿进程,什么是僵尸进程,如何解决僵尸进程
得分点
父进程先结束、占用系统资源、wt()、wtpid()
标准回答
-
- 孤儿进程 孤儿进程是指一个父进程退出后,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并且由 init 进程对它们完整状态收集工作,孤儿进程一般不会产生任何危害。
-
- 僵尸进程 僵尸进程是指一个进程使用 fork() 函数创建子进程,如果子进程退出,而父进程并没有调用 wt() 或者wtpid() 系统调用取得子进程的终止状态,那么子进程的进程描述符仍然保存在系统中,占用系统资源,这种进程称为僵尸进程。
-
- 解决僵尸进程 一般,为了防止产生僵尸进程,在 fork() 子进程之后我们都要及时在父进程中使用 wt() 或者 wtpid() 系统调用,等子进程结束后,父进程回收子进程 PCB 的资源。 同时,当子进程退出的时候,内核都会给父进程一个 SIGCHLD 信号,所以可以建立一个捕获 SIGCHLD 信号的信号处理函数,在函数体中调用 wt() 或 wtpid(),就可以清理退出的子进程以达到防止僵尸进程的目的。
7、请你说说 C++ Lambda 表达式用法及实现原理
答案字数太多,必免看了烦,建议自行查询!!!
8、请你说说 innodb 和 myisam 的区别
简述:
InnoDB:支持事务、行锁和外键,批量插入慢,空间、内存消耗高。 MyIsAM:不支持事务、不支持行锁,只支持表锁,不支持外键,批量插入快,空间、内存消耗较低。
得分点 事务、锁、读写性能、存储结构 标准回答 InnoDB是具有事务、回滚和崩溃修复能力的事务安全型引擎,它可以实现行级锁来保证高性能的大量数据中的并发操作;MyISAM是具有默认支持全文索引、压缩功能及较高查询性能的非事务性引擎。具体来说,可以在以下角度上形成对比: 事务 :InnoDB支持事务;MyISAM不支持。 数据锁 :InnoDB支持行级锁;MyISAM只支持表级锁。 读写性能 :InnoDB增删改性能更优;MyISAM查询性能更优。 全文索引 :InnoDB不支持(但可通过插件等方式支持);MyISAM默认支持。 外键 :InnoDB支持外键;MyISAM不支持。 存储结构 :InnoDB在磁盘存储为一个文件;MyISAM在磁盘上存储成三个文件(表定义、数据、索引)。 存储空间 :InnoDB需要更多的内存和存储;MyISAM支持支持三种不同的存储格式:静态表(默认)、动态表、压缩表。 移植 :InnoDB在数据量小时可通过拷贝数据文件、备份 binlog、mysqldump工具移植,数据量大时比较麻烦;可单独对某个表通过拷贝表文件移植。 崩溃恢复 :InnoDB有崩溃恢复机制;MyISAM没有。 默认推荐 :InnoDB是MySQL5.5之后的默认引擎。 加分回答 InnoDB中行级锁是怎么实现的? InnoDB行级锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。 当表中锁定其中的某几行时,不同的事务可以使用不同的索引锁定不同的行。另外,不论使用主键索引、唯一索引还是普通索引,InnoDB都会使用行锁来对数据加锁。 |
---|
9、请你说说数据库的索引是什么结构,为什么不用哈希表?
得分点
B+树、内存资源
标准回答
MySQL中的索引B+树实现的; 哈希表的查询效率的确最高,时间复杂度O(1),但是它要求将所有数据载入内存,而数据库存储的数据量级可能会非常大,全部载入内存基本上是不可能实现的; B+树可以分段加载需要的节点数据,可以在内存资源有限的前提下,极大提高查询效率
10、虚析构函数有什么作用?
得分点
概念、防止内存泄露
标准回答
-
- 概念 虚析构函数,是将基类的析构函数声明为 virtual class Base { public: Base() { } // 虚析构函数 virtual ~Base() { } }
-
- 作用 虚析构函数的主要作用是为了防止遗漏资源的释放,防止内存泄露。如果基类中的析构函数没有声明为虚函数,基类指针指向派生类对象时,则当基类指针释放时不会调用派生类对象的析构函数,而是调用基类的析构函数,如果派生类析构函数中做了某些释放资源的操作,则这时就会造成内存泄露。
11、说一说常用的 Linux 命令?(常见)
标准回答
常用的 Linux 命令有:
cd:切换当前目录
ls:查看当前文件与目录
grep:通常与管道命令一起使用,用于对一些命令的输出进行筛选加工
cp:复制文件或文件夹
mv:移动文件或文件夹
rm:删除文件或文件夹
ps:查看进程情况
kill:向进程发送信号
tar:对文件进行打包
cat:查看文件内容
top:查看操作系统的信息,如进程、CPU占用率、内存信息等(实时) free:查看内存使用情况
pwd:显示当前工作目录
12、简述一下堆和栈的区别
得分点
管理方式、空间大小、是否产生内存碎片、生长方向、分配方式、分配效率
标准回答
堆和栈主要有如下几点区别:管理方式、空间大小、是否产生内存碎片、生长方向、分配方式、分配效率。
-
- 管理方式 对于栈来讲,是由编译器自动管理,无需手动控制;对于堆来说,分配和释放都是由程序员控制的。
-
- 空间大小 总体来说,栈的空间是要小于堆的。堆内存几乎是没有什么限制的;但是对于栈来讲,一般是有一定的空间大小的。
-
- 碎片问题 对于堆来讲,由于分配和释放是由程序员控制的(利用new/delete 或 malloc/free),频繁的操作势必会造成内存空间的不连续,从而造成大量的内存碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的数据结构,在某一数据弹出之前,它之前的所有数据都已经弹出。
-
- 生长方向 对于堆来讲,生长方向是向上的,也就是沿着内存地址增加的方向,对于栈来讲,它的生长方式是向下的,也就是沿着内存地址减小的方向增长。
-
- 分配方式 堆都是动态分配的,没有静态分配的堆。栈有两种分配方式:静态分配和动态分配,静态分配是编译器完成的,比如局部变量的分配;动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器实现的,无需我们手工实现。
-
- 分配效率 栈是机器系统提供的数据结构,计算机会在底层对栈提供支持,分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率很高。堆则是 C/C++ 函数提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率要比栈底的多。
13、请你说说重载,复写,隐藏的区别
得分点
定义、作用域、有无 virtual、函数名、形参列表、返回值类型
标准回答
重载、重写、隐藏在定义、作用域、有无 virtual、函数名、形参列表、返回值类型等方面有区别。
-
- 重载:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载,与返回值类型无关。
-
- 重写:指不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。
隐藏的实质是:在函数查找时,名字查找先于类型检查。如果派生类中成员和基类中的成员同名,就隐藏掉。编译器首先在相应作用域中查找函数,如果找到名字一样的则停止查找。
- 重写:指不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。
-
- 派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。
14、请你说说各数据类型 sizeof 是多少,sizeof 指针是多少,sizeof 原理
标准回答
-
- 各数据类型 sizeof 的结果其实就是该数据类型的字节数,不同类型的数据 sizeof 的结果是不一样的,并且不同的操作系统和编译器下同一数据类型的 sizeof 的结果也不一样,具体看编译器是如何实现的。以下是Microsoft C++ 中对常见内置类型 sizeof 的代码: #include using namespace std; int mn() { cout << sizeof(bool) << endl; cout << sizeof(char) << endl; cout << sizeof(short) << endl; cout << sizeof(int) << endl; cout << sizeof(long) << endl; cout << sizeof(long long) << endl; return 0; } 运行结果: 1 1 2 4 4 8
-
- 对指针变量进行 sizeof 运算,获得的是指针变量的大小,而无论是什么类型的指针,在同一平台下结果都是一样的。在 32 位平台下是 4 个字节,在 64 位平台下是 8 个字节。
-
- sizeof 的原理:sizeof 是在编译的时候,查找符号表,判断类型,然后根据基础类型来取值。如果 sizeof 运算符的参数是一个不定长数组,则该需要在运行时计算数组长度。
15、说说 const 和 define 的区别
得分点
作用、编译阶段、预处理阶段、简单替换、类型检查、内存 标准回答 const 在 C 语言中表示只读,编译器禁止对它修饰的变量进行修改,在 C++ 中增加了常量的语义。而 define 用于定义宏,而宏也可以用于定义常量。
它们的区别有:
- const 生效于编译阶段,而 define 生效于预处理阶段;
- define只是简单的字符串替换,没有类型检查,而 const 有对应的数据类型,编译器要进行判断的,可以避免一些低级的错误;
- 用 define 定义的常量是不可以用指针变量去指向的,用 const 定义的常量是可以用指针去指向该常量的地址的;
- define 不分配内存,给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大,const 在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝;
- 可以对 const 常量进行调试,但是不能对宏常量进行调试。
16、请你说说 Redis 如何与数据库保持双写一致性
得分点
四种同步策略及其可能出现的问题,重试机制
标准回答
保证缓存和数据库的双写一致性,共有四种同步策略,即先更新缓存再更新数据库、先更新数据库再更新缓存、先删除缓存再更新数据库、先更新数据库再删除缓存。 先更新缓存的优点是每次数据变化时都能及时地更新缓存,这样不容易出现查询未命中的情况,但这种操作的消耗很大,如果数据需要经过复杂的计算再写入缓存的话,频繁的更新缓存会影响到服务器的性能。如果是写入数据比较频繁的场景,可能会导致频繁的更新缓存却没有业务来读取该数据。 删除缓存的优点是操作简单,无论更新的操作复杂与否,都是直接删除缓存中的数据。这种做法的缺点则是,当删除了缓存之后,下一次查询容易出现未命中的情况,那么这时就需要再次读取数据库。 那么对比而言,删除缓存无疑是更好的选择。 那么我们再来看一下先操作数据库和后操作数据库的区别;先删除缓存再操作数据库的话,如果第二步骤失败可能导致缓存和数据库得到相同的旧数据。先操作数据库但删除缓存失败的话则会导致缓存和数据库得到的结果不一致。出现上述问题的时候,我们一般采用重试机制解决,而为了避免重试机制影响主要业务的执行,一般建议重试机制采用异步的方式执行。当我们采用重试机制之后由于存在并发,先删除缓存依然可能存在缓存中存储了旧的数据,而数据库中存储了新的数据,二者数据不一致的情况。 所以我们得到结论:先更新数据库、再删除缓存是影响更小的方案。如果第二步出现失败的情况,则可以采用重试机制解决问题。
17、请你说说C++引用的概念
得分点
什么是引用、基本语法、注意事项、本质、应用
标准回答
- 引用(Reference)是 C++ 相对于 C 语言的一个扩充。引用可以看做是数据的一个别名,通过这个别名和原来的名字都能够找到这份数据。引用类似于 Windows 中的快捷方式,一个可执行程序可以有多个快捷方式,通过这些快捷方式和可执行程序本身都能够运行程序;引用还类似于人的绰号(笔名),使用绰号(笔名)和本名都能表示一个人。
- 基本语法 typename & ref = varname;
- 使用引用的注意事项: - 引用必须引用合法的内存空间 - 引用在定义时必须初始化 - 引用一旦初始化后,就不能再引用其它数据 - 引用在定义时需要加上 &,在使用时不能加 &,使用时加 & 表示取地址 - 函数中不要返回局部变量的引用
- 引用的本质是指针,低层的实现还是指针。
18、请你说说迭代器失效原因,有哪些情况
标准回答
STL 中某些容器调用了某些成员方法后会导致迭代器失效。例如 vector 容器,如果调用 reserve() 来增加容器容量,之前创建好的任何迭代器(例如开始迭代器和结束迭代器)都可能会失效,这是因为,为了增加容器的容量,vector 容器的元素可能已经被复制或移到了新的内存地址。
- 序列式容器迭代器失效 对于序列式容器,例如 vector、deque,由于序列式容器是组合式容器,当当前元素的迭代器被删除后,其后的所有元素的迭代器都会失效,这是因为 vector、deque都是连续存储的一段空间,所以当对其进行 erase 操作时,其后的每一个元素都会向前移一个位置。解决:erase 返回下一个有效的迭代器。
- 关联式容器迭代器失效 对于关联容器,例如如 map、 set,删除当前的迭代器,仅仅会使当前的迭代器失效,只要在 erase 时,递增当前迭代器即可。这是因为 map 之类的容器,使用了红黑树来实现,插入、删除一个节点不会对其他点造成影响。erase 迭代器只是被删元素的迭代器失效,但是返回值为 void,所以要采用 erase(iter++) 自增方式删除迭代器。
19、请你说说动态库静态库的区别和优缺点
得分点
命名方式、链接、内存、更新
标准回答
静态库和动态库的区别:
- 命令方式不同 - 静态库命名 Linux : libxxx.a lib : 前缀(固定) xxx : 库的名字,自己起 .a : 后缀(固定) Windows : libxxx.lib - 动态库命名 Linux : libxxx.so lib : 前缀(固定) xxx : 库的名字,自己起 .so : 后缀(固定) Windows : libxxx.dll
- 链接时间和方式不同 - 静态库的链接是将整个函数库的所有数据在编译时都整合进了目标代码 - 动态库的链接是程序执行到哪个函数链接哪个函数的库 静态库和动态库的优缺点:
-
- 静态库优缺点 - 优点:发布程序时无需提供静态库,移植方便,运行速度相对快些 - 缺点:静态链接生成的可执行文件体积较大,消耗内存,如果所使用的静态库发生更新改变,程序必须重新编译,更新麻烦。
-
- 动态库优缺点 - 优点:更加节省内存并减少页面交换,动态库改变并不影响使用的程序,动态函数库升级比较方便 - 缺点:发布程序时需要提供动态库
20、请你说说虚函数可以是内联函数吗
得分点
编译期、运行期
标准回答
- 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
- 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时不可以内联。
- inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类,这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。
结束语 🥇🥇🥇
发现非常好用的一个刷题网站,可以检测大家的基础!大家一起努力!加油!!!
包含数据库、Java、C++、C、Python、前端等等题目,难度可以自行选择
在线编程出答案,(也可自行查看答案),也有选择题,非常方便
程序员刷题神器网站点击链接注册即可刷题
祝大家早日找到满意的工作!