c++面试题(基础篇)

xiaoxiao2021-02-28  26

● STL标准模板库

(1)vector的底层存储机制:vector是一个动态数组,里面有一个指针指向一片连续的内存,当空间不够时就用动态重新分配 ,一般是当前大小的两倍,然后把原来的数据拷贝过去,释放原来的地址空间,当删除里面的数据时,它的存储空间是不释放的,仅仅是清空了里面的数据。

(2)list的底层存储机制:list是以结点为单位存放数据,数据在内存中不一定连续。

(3)vector可以随机存储元素,但是如果在非尾部插入删除数据时,它需要对现有数据进行复制移动,效率很低,它比较适合对象简单,数量变化不大,随机访问频繁的情况;list不支持随机存储,插入和删除数据时,只需要对现有数据进行遍历效率很高,它适用于对象大,数量变化频繁,经常插入和删除的情况。

(4)deque动态地以分段连续空间组合而成。平时使用时尽量使用vector.

(5)map是一种平衡二叉搜索树,通过键值可以修改它的真实值。

(6)set是集合,不允许重复,multiset允许重复。

(7)迭代器能够按照顺序访问某个容器包含的元素,它无需暴露该容器的数据结构,它将容器和算法分开让两者独立设计。

●智能指针

概念:对于编译器来说智能指针实际上是一个栈对象,并非指针类型,在栈生命期即将结束时通过析构函数释放它管理的堆内存。访问智能指针可以用get函数。

使用原因:能解决内存泄露,避免出现野指针,还有能解决比较隐蔽的异常分支导致的资源泄漏。

shared_ptr:共享指针,多个智能指针可以指向同一个对象,该对象或其相关资源会在最后一个引用被销毁时释放。

unique_ptr:唯一指针,同一时间只有一个智能指针可以指向该对象。

基于引用计数的智能指针具体实现(伪代码):

template<classT>

class SmartPtr

{

       //一般会有两个构造函数

       SmartPtr(T *p);

       SmartPtr(const Smart<T> &orig);//浅拷贝(复制构造函数)

       {

              ptr = orig.ptr;

              user_count = orig.user_count;

              user_count ++;

       }

       operator -> = //重载操作符

        ~SmartPtr();//析构函数

       {

              if(user_count == 0)

              {

                     delete //释放

              }

       }

       T* ptr;

       int user_count;//引用计数

}

●C++程序的内存分配是怎么样的

c++的内存分配分为五个部分,分别是栈区,堆区,全局区,文字常量区,程序代码区,栈区是由编译器自动分配释放的,像函数参数局部变量什么的都是存放在栈区的,堆区是由程序员主动分配释放的,全局区主要存放全局变量和静态变量,常量字符串存放在文字常量区,程序代码区就是存放程序的二进制代码的。

●内存泄露是如何造成的

一个是程序员申请的内存没有释放,像malloc, new出来的对象都需要主动去释放,第二个就是一些无主内存,无主内存是修改指针的指向造成的,修改指针值后,原来的内存就无法释放了,所以在修改指针前要记得释放原来的内存。还有可能出现的地方是程序出现异常,打乱了正常执行的顺序,所以要在异常处理的代码中加上内存资源的释放。

●内存泄漏如何检测

第三方工具Visual Leak Detector(下面简称vld),还有visual studio自带的memoryleaks会在output信息窗口输出内存泄漏的地址。

●如何防止内存泄漏

第一就是要记得主动释放,不要随意去修改指针地址,尽量使用智能指针,也可以自己封装一个这样的类,用计数去管理当前使用的指针,当指针计数为0时就去主动释放。

●内存池

内存池是为了解决一些程序频繁的进行内存的分配和释放操作,这样会产生大量的内存碎片,导致性能急速下降。内存池在使用内存之前先申请分配一定数量大小相等的内存块留作备用,当有新的内存需求时,就从内存池中分出一部分内存块,只有在内存池大小需要扩展时才会去调用内存分配函数,这样做的好处就是使得内存分配效率很高。

●怎么理解组件的概念

组件就是一些可以执行的二进制程序,它可以给其他的应用程序、操作系统或其他组件提供功能。(其实它也是一种开发模式,组件不是某项研发技术,它是一种编程思想的变化,它不局限于某种语言,就像我们从原来的面向结构化编程到面向对象编程一直到最后的面向组件的编程模式。)COM是开发组件的一种方法,也是一种标准和编程规范,不论什么语言要实现组件都必须按照这种规范来实现。符合COM标准的对象就是COM对象。COM对象无非是实现了很多接口的对象而已。这些编程规范定义了组件的操作、接口的访问等等。

●COM编程的优越性

因为COM实现了分层次的编程使得软件的利用率很高,并且在编程难度和工作量上都会降低,开发周期变短,成本也会降低。

●COM组件与一般DLL的区别

COM是以接口对功能进行分类,便于组织,升级维护,功能扩充的话只需要添加接口就行了,它还可以以轻松的实现进程间的调用和分布式调用,而DLL一般只是一大堆函数,服务升级也比较困难,函数不能随意改变.

●ADO编程

步骤:先初始化库环境,创建Connection对象,建立数据源的连接,执行Sql命令,然后使用结果集,终止连接。最重要的三个对象:

连接对象(Connection):用来管理应用程序和数据库之间的连接

命令对象(Command):用来处理重复执行的查询

记录集对象(RecordSet):用来获取数据,存放查询的结果

分别对应三个智能指针:_ConnectionPtr,_CommandPtr,_RecordsetPtr.

●四种数据库访问技术

(1)ODBC 开放式数据库连接

(2)DAO 是MicroSoft提供的基于数据库对象集合的访问技术

(3)OLE DB 是基于COM接口技术的

(4)ADO是对OLEDB的封装,简单易用

 

●动态链接和静态链接

动态链接库就是exe程序在程序执行的时候动态加载的,而静态链接库是在编译时将其编译在代码之中。

●WindowsHook

钩子实际上是一个处理消息的程序段,它可以监视指定窗口的某种消息,在截取到消息后在目标窗口处理函数之前处理它。也就是将钩子函数放在DLL中,消息经过钩子函数过滤,也可以不用处理继续传递该消息,也可以强制结束消息传递。

运行机制:每一个Hook都有一个与之相关联的指针列表(钩子链表),由系统维护,列表指针指向被Hook子程调用的回调函数。最近安装的钩子放在链的开始,也就是后加入的先获得控制权。

线程钩子:监视指定线程的事件消息。

系统钩子:监视系统中所有线程的事件消息,钩子函数必须放在独立的DLL中。

两类钩子都安装了会优先调用线程钩子,然后再调用系统钩子。

转载请注明原文地址: https://www.6miu.com/read-2250204.html

最新回复(0)