目录
1.C++的虚函数的作用是什么?原理是什么?
2. .C++中 const、define、static 的区别?
3. 对于epoll底层结构和原理有什么了解?
4. epoll 的 ET 模式和 LT 模式哪个更高效?
5.什么情况下C++STL迭代器会失效?
6.什么是右值引用?和移动语义、完美转发有身什么联系?
7.什么是只能指针?有什么类别和对应的应用场景?
8.vector 和 list 有什么区别?
9. C++内存 New 与 malloc 区别是什么?
10. 常量指针和指针常量有什么区别?
11. map 和 unordered_map 的区别以及适用场景?
12. 静态链接库和动态链接库各自的优缺点分别是什么?
13. C++虚函数、纯虚函数的区别和适用场景?
14. 是否遇到过 C++变量冲突的情况?如何解决?
15 .聊聊 C++的内存碎片?
16. C++中 lambda 表达式的捕捉变量的方式有哪几种?
17. C++中 final 关键字是做什么的?能否想到更多作用?
18. 对 RPC 框架有什么了解?原理?
19 .C++中析构函数是否必须为虚函数?构造函数呢?
20.用过什么 C++的新特性?
21. C++中 make_unique 和 unique_ptr 有什么区别?
22. 聊聊 C++的内存分区?
23. .C++中非静态数据成员可以被静态成员函数直接访问吗?
答:面向对象语言有三个最基本的特征就是:继承,多态, 封装。而在 c++ 语言中,这种多态的特征就是通过虚函数 (virtual)来实现的。所以 C++ 中的虚函数的作用主要是实现了多态的机制(当调用一个虚函数时,被执行的代码必须和 调用函数的对象的动态类型相一致)。 虚函数(Virtual Function)是通过一张虚函数表(Virtual Table) 来实现的。简称为 V-Table 。在这个表中,主是要一个类的虚 函数的地址表,这张表解决了继承、覆盖的问题,保证其容 真实反应实际的函数。
答: const 是单词 constant 的简写,字面意思是常数、常量。 用于变量修饰,表明这个变量不能被修改;用于指针修饰, 表明指针的指向物不能被修改;用于方法修饰,表明这个方法不会对对象造成改变。 #define 和另外两个不一样,它属于宏,是预处理器的一部分。 预处理是在编译之前的一道,简单地进行字符串替换,它不 按照语言的语法,而是直管自己的语法。 static 的定义相对较复杂,用在全局变量,表明这个变量在每 个编译单元有独自的实例;用在函数里的局部变量,表明它 的生存周期其实是全局变量,但仅在函数内可见;用在类成员,表明成员或者方法是类的,而不是对象实例的。
答: epoll 一种网络模式,采用的是 IO 多路复用技术,速度 相对较快。 而 Linux 内核提供了 3 个关键函数供用户来操作 epoll ,分别 是:epoll_create(), 创建 eventpoll 对象; epoll_ctl(), 操作 eventpoll 对象; epoll_wait(), 从 eventpoll 对象中返回活跃的 事件。
答: ET 模式更加高效。与 poll 的事件宏相比, epoll 新增了一个事件宏 EPOLLET , 这就是所谓的边缘触发模式(Edge Trigger , ET ),而默认的模式称为水平触发模式(Level Trigger, LT )。 对于水平触发模式,一个事件只要有,就会一直触发; 对于边缘触发模式,只有一个事件从无到有才会触发。
答:当容器调用 erase() 方法后,当前位置到容器末尾元素的 所有迭代器全部失效。 当容器调用 insert() 方法后,当前位置到容器末尾元素的所有 迭代器全部失效。 如果容器扩容,在其他地方重新又开辟了一块内存。原来容器底层的内存上所保存的迭代器全都失效了。
答:右值引用是 C++11 引入的一个重要的技术,它是移动语 义(Move semantics )与完美转发(Perfect forwarding)的基 石。 定义右值引用需要使用&&、右值引用一定不能被左值所初始 化,只能用右值初始化。
答:智能指针的作用是管理一个指针,因为存在以下这种情 况:申请的空间在函数结束时忘记释放,造成内存泄漏。使 用智能指针可以很大程度上的避免这个问题,因为智能指针 是一个类,当超出了类的实例对象的作用域时,会自动调用 对象的析构函数,析构函数会自动释放资源。所以智能指针 的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。 unique_ptr 实现独占式拥有或严格拥有概念,保证同一时间 内只有一个智能指针可以指向该对象。它对于避免资源泄露特别有用。 shared_ptr 实现共享式拥有概念。多个智能指针可以指向相 同对象,该对象和其相关资源会在“ 最后一个引用被销毁 ” 时 候释放。weak_ptr 是一种不控制对象生命周期的智能指针 , 它指向 一个 shared_ptr 管理的对象 . 进行该对象的内存管理的是 那个强引用的 shared_ptr , weak_ptr 只是提供了对管理对象的一个访问手段。
答: stl 提供了三个最基本的容器: vector,list,deque 。 vector 和 built-in 数组类似,它拥有一段连续的内存空间,并 且起始地址不变,因此它能非常好的支持随即存取,即[] 操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。 这些都大大影响了 vector 的效率。 list 就是数据结构中的双向链表 ( 根据 sgi stl 源代码 ) ,因此它的内存空间可以是不连续的,通过指针来进行数据的访问, 这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的 效率支持任意地方的删除和插入。vector 适用:对象数量变化少,简单对象,随机访问元素频 繁;list 适用:对象数量变化大,对象复杂,插入和删除频
- 申请的内存所在位置;
- 返回类型安全性;
- 内存分配失败时的返回值;
- 是否需要指定的内存大小;
- 是否调用构造函数/析构函数;
- 对数组的处理;
- 是否可以被重载;
- 能够直观的重新分配内存;
- 客户处理内存分配不足;
答:常量指针又叫常指针,可以理解为常量的指针,也即这 个是指针,但指向的是个常量,这个常量是指针的值(地址), 而不是地址指向的值。 指针常量本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
答:主要是需要引入的头文件不同和内部实现机理不同。 对于那些有顺序要求的问题,用 map 会更高效一些; 对于查找问题,unordered_map 会更加高效一些,因此遇到查找问题,常会考虑一下用 unordered_map 。
答:静态链接库的优点:代码装载速度快,执行速度略比动 态链接库快; 只需保证在开发者的计算机中有正确的.LIB 文 件,在以二进制形式发布程序时不需考虑在用户的计算机上 .LIB 文件是否存在及版本问题,可避免 DLL 地狱等问题。 静态链接库的缺点:使用静链接生成的可执行文件体积较 大,包含相同的公共代码,造成浪费。动态链接库的优点:更加节省内存并减少页面交换;DLL 文件与 EXE 文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换 DLL 文件不会对 EXE 文件造成任何影响,因而极大地提高了可维护性和可扩展性;不同编程语言编写的程序只要按照函数调用约定就可以调用同一个 DLL 函数;动态链接库的缺点:使用动态链接库的应用程序不是自完备 的,它依赖的 DLL 模块也要存在,如果使用载入时动态链接, 程序启动时发现 DLL 不存在,系统将终止程序并给出错误信 息。而使用运行时动态链接,系统不会终止,但由于 DLL 中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常见。
答:虚函数,在类成员方法的声明(不是定义)语句前加 “virtual”, 如 virtual void func() ;对于虚函数,子类可以(也 可以不)重新定义基类的虚函数,该行为称之为复写 Override 。 纯虚函数,在虚函数后加“=0” ,如 virtual void func()=0 ;对 于纯虚函数,子类必须提供纯函数的个性化实现。 当基类中的某个成员方法,在大多数情形下都应该由子类提 供个性化实现,但基类也可以提供缺省备选方案的时候,该 方法应该设计为虚函数。 当基类中的某个成员方法,必须由子类提供个性化实现的时候,应该设计为纯虚函数。
答:换变量名;应用命名空间;命名空间嵌套;加入类前缀 作为成员变量
答:内存碎片即“碎片的内存”,它分为外碎片和内碎片, 内存碎片描述一个系统中所有不可用的空闲内存。 这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用。 这一问题的发生,原因在于这些空闲内存小且以不连续方式 出现在不同的位置。因此这个问题的或大或小取决于内存管理算法的实现上。
答:类似参数传递方式(值传递、引入传递、指针传递), 在 Lambda 表达式中,外部变量的捕获方式也有值捕获、引用捕获、隐式捕获。
答: C++11 引入了关键字 final ,按官方的标准是该关键字是 用来标识虚函数不能在子类中被覆盖(override) ,或一个类不能被继承。
答: RPC 可以分为两部分:用户调用接口 + 具体网络协议。 前者为开发者需要关心的,后者由框架来实现。
答: C++ 默认的析构函数不是虚函数是因为虚函数需要额外 的虚函数表和虚表指针,占用额外的内存。而对于不会被继 承的类来说,其析构函数如果是虚函数,就会浪费内存。因此 C++ 默认的析构函数不是虚函数,而是只有当需要当作父类时,设置为虚函数。虚函数对应一个 vtable ,可是这个 vtable 其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable 来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable 呢?所以构造函数不能是虚函数。
答: auto & decltype 、左值右值、 std::function & std::bind & lambda 表达式、并发、智能指针等等。
答: make_unique 只是把参数转发给要创建对象的构造函数, 再从 new 出来的原生指针构造 std::unique_ptr; 在这里等效于 unique_ptr new ; auto_ptr 好像没有这样的方法。
答:在 C++ 中,内存分为五个区,它们分别是栈区、堆区、 全局区/ 静态区、字符串常量和代码区。 栈区(stack) — 由编译器自动分配释放 ,存放函数的参数 值,局部变量的值等。其操作方式类似于数据结构中的栈。 堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。注意它与数据结构中的堆是两 回事,分配方式倒是类似于链表。全局区(静态区)(static) — ,全局变量和静态变量的存储 是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一 块区域。 - 程序结束后由系统释放。 文字常量区 — 常量字符串就是放在这里的。 程序结束后由系统释放。程序代码区— 存放函数体的二进制代码。
答:不可以。静态成员函数只是和类实现了绑定,而没有和 任何对象绑定在一起,不包含 this 指针,无法访问静态成员。 (静态成员函数所需内存在程序执行前就分配好了,给静态 成员必须要等到这个类在堆/ 栈上分配内存才能使用,所以如 果静态成员函数访问非静态,可能非静态成员还没有内存)。