1、介绍STL,说明STL如何实现vector。
STL,标准模板库。由容器算法迭代器组成。
优点:容易实现搜索数据或对数据排序等一系列算法。
vector实质是一个动态数组,会根据数据增加,动态增加数组空间。
2、分析Visual C++程序出错的原因:
(1)所引用的函数、变量不存在、拼写不正确或者使用错误。 (2)使用了不同版本的链接库。
3 继承 vs. 多态
在OOD和OOP中,多态组合优于继承。多态的基础是继承,没有继承,多态无从谈起。
(1)对象的类型不影响类中函数的行为时,就要使用模板来生成这样的一组类。
(2)对象的类型影响类中函数的行为时,就要使用继承来得到这样一组类。
4 指针vs. 引用
(1)引用在创建的同时必须初始化,即引用到一个有效对象,而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。
(2)不存在NULL引用,引用必须与合法的存储单元关联; 而指针可以是NULL。
(3)引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用; 指针再任何时候都可以改变为指向另一个对象。给引用赋值并不是改变它和原始对象的绑定关系。
(4)引用的创建和销毁并不会调用类的拷贝构造函数。
(5)引用的用法和对象一样。在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮助完成转换。
传引用比传指针安全?为什么?
不存在空引用,引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象引用,显得很安全。const指针仍然存在空指针,并且有可能产生野指针。
引用既具有指针效率,又具有变量使用的方便性和直观性。
5 参数传递方式
(1)传值方式适合一般数值传送,且不改变原数据,要消耗内存空间。
(2)传指针方式适合传递数组、指针,传递的是地址,直接操作会改变原数据。
(3)引用方式和指针比较类似,一般情况下能用传址的就用引用,使用引用更方便。
6 如何应用设计模式的理念
设计模式更多考虑的是扩展和重用,这两方面在很多情况下,往往会被忽略。
虽然设计模式非常关键,不建议程序员滥用设计模式,因为不合适的使用设计模式可能使得简单问题变得更发杂。
7 设计模式理解
设计模式广义概念由建筑设计师提出:每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次使用该方案而不必重复劳动。
将设计模式应用到面向对象软件领域内,形成对设计模式的狭义定义。
设计模式就是解决某个特定的面向对象软件问题的特定方法,并且已经上升到理论程度。
框架 VS. 设计模式
(1)两者对问题区域不同。设计模式针对面向对象的问题域,而框架针对特定业务的问题域。
(2)设计模式比框架更为抽象。设计模式在碰到具体问题后,才能产生代码;框架已经可以用代码表示。
(3)设计模式是比框架更小的体系结构元素。框架中可以包括多个设计模式。
设计模式是招式,将招式组合起来称为框架,所以框架是套路,框架是个半成品。
8 C++与C定义结构的区别
C中的结构仅仅是数据的结构,C中的结构体是不能声明函数的。
C++中的结构和类具备一样功能,结构体内可以声明函数。C++的结构体和类默认具有不一样的访问属性。
C++中结构体和类的区别
C++类中,对于未指定访问控制属性的成员,其访问控制属性为私有类型(private);结构体中,对于未指定任何访问控制属性的成员,其访问控制属性为公有类型(public)
C语言中,空结构体的大小为0,而C++中空结构体(属于空类)的大小为1。
C++中空类的大小为1的原因:
空类也可以实例化,类实例化出的每个对象都需要有不同的内存地址,为使每个对象在内存中的地址不同,所以在类中会加入一个隐含的字节。
9 构造函数和析构函数
构造函数不能为虚函数,要构造一个对象,必须清楚地知道要构造什么,否则无法构造一个对象。析构函数可以是虚函数,也可以是纯虚函数。
10 对拷贝构造函数的深拷贝、浅拷贝和临时对象理解
深拷贝意味拷贝了资源和指针,而浅拷贝只是拷贝了指针,没有拷贝资源。这样会使得两个指针指向同一份资源,造成对同一份析构两次,程序崩溃。
临时对象的开销会比局部对象小些。
浅拷贝和深拷贝的区别?
对深拷贝与浅拷贝的再次理解
在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
11 基类中有一个虚函数,子类还需要申明virtual吗
这种情况下不申明virtual是没有关系的。有的程序员会喜欢用显示申明,这样可以使得代码更为清晰,增强可读性。
C++中基类的虚函数的一些思考
C++中的虚函数总结
12 C++的类有什么优点
C++可以通过封装某些函数功能实现重用,那么C++的类有什么优点?是不是仅仅为了实现重用?
C++类并不是仅仅为了重用。(Object Oriented Design,简称OOD面向对象)和(Object Oriented Programming,简称OOP面向对象的程序设计)从根本上改变了程序设计模式和设计思想。
类的三大特征:封装、继承、多态。这些特征将编程模型从面向过程转变为面向对象,从根本上改变了编写程序的思路和模式。
13 如何实现多态?
多态的基础是继承,需要虚函数的支持。C++中多态有以下3种实现方式:使用函数重载、使用模板函数、使用虚函数。
子类继承父类的大部分资源。不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数。
14 为什么引入抽象基类和纯虚函数?
主要是为了实现一种接口。在面向对象编程语言中,为了更好表示客观世界,所以有些类可以什么都不实现只是提供一个共享的接口。这就是纯虚函数,而含有纯虚函数的基类就是抽象基类。
15 模板和容器,如何实现?
泛型编程实质上就是模板编程,体现的是一种通用和泛化的思想。
STL中有7种主要容器:vector,list,deque,map,multimap,set,multiset。
16 MVC?简单举例说明其应用?
MVC模式是Observer模式的一个特例,典型的有MFC中的文档视图架构。
17 列举几种进程的同步机制?
原子操作、信号量机制、自旋锁、管程、会合、分布式系统。
(a)一个信号量只能置一次初值,以后只能对之进行p操作或v操作。由此也可以看到,信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。信号量机制功能强大,但使用时对信号量的操作分散, 而且难以控制,读写和维护都很困难。加重了程序员的编码负担;核心操作P-V分散在各用户程序的代码中,不易控制和管理;一旦错误,后果严重,且不易发现和纠正。 (b)自旋锁: 旋锁是为了保护共享资源提出的一种锁机制。调用者申请的资源如果被占用,即自旋锁被已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁。自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起以下两个问题; 1、死锁 2、过多地占用CPU资源 传统自旋锁由于无序竞争会导致“公平性”问题 (c)管程: 信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。 (d)会合: 进程直接进行相互作用 (e)分布式系统: 由于在分布式系统中没有公共内存,因此参数全为值参,而且不可为指针。 (f)原语 是不可中断的过程。
18 进程之间通信途径?
共享存储系统、消息传递系统、管道(以文件系统为基础)
19 进程死锁的原因?
死锁是指在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它们现在保持着的资源,否则就不能向前推进。此时,每个进程都占用了一定的资源但又不能向前推进,称这一组进程产生了死锁。简单说,就是两个或多个进程无止境的等候着,永远不会成立的条件的一种系统状态。
产生死锁的根本原因是系统能够提供的资源个数比要求该资源的进程数少,具体原因:系统资源不足、进程推进顺序非法。
20 死锁的4个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在没使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程间形成一种头尾相接的循环等待资源关系。
21 死锁的处理?
(1)鸵鸟算法忽略该问题、
(2)检测死锁并且恢复、
(3)仔细对资源进行动态分配,以避免死锁、
(4)通过破坏死锁产生的4个必要条件之一,来防止死锁。
22 操作系统中的进程调度策略?
FCFS(先来先服务)、优先级、时间片轮转、多级反馈。
23 类的静态成员和非静态成员区别?
静态变量使用static修饰符进行声明,在类被实例化时创建,通过类进行访问不带有static修饰符声明的变量称作非静态变量。对象被实例化时创建,通过对象进行访问一个类的所有实例的同一静态变量都是同一个值,同一个类的不同实例的同一非静态变量可以是不同的值。
静态函数的实现里不能使用非静态成员,例如非静态变量、非静态函数。
类的静态成员每个类只有一个,非静态成员每个对象一个。
24 纯虚函数如何定义?
virtual void f()=0;
此函数是一个接口,子类定义时必须要实现该接口。在C/C++语言中,纯虚函数的应用非常广泛。
25 数组和链表的区别
数组:数据顺序存储,固定大小。
链表:数据可以随机存储,大小可以动态改变。
26 ISO的七层模型是什么?TCP/UDP属于那一层?TCP/UDP有哪些优缺点?
ISO的七层模型包括:应用层、表示层、会话层、运输层、网络层、物理链路层、物理层。
TCP/UDP属于传输层。
TCP服务提供了数据流传输,可靠性,有效流控制,全双工操作和多路复用技术等。UDP并不提供对IP协议的可靠机制、流控制以及错误恢复功能等。由于UDP比较简单,所以它的头部信息包含很少的字节,比TCP负载消耗少。
TCP:提供稳定传输服务,有流量控制,缺点是包头大,冗余性不好。
UDP:不提供稳定服务,包头部信息少,开销小。
27 内存分配方式及其区别
(1)从静态存储区域分配:内存在程序编译的时候已经分配好,这块内存在程序的整个运行期间都存在。如全局变量和static变量。
(2)栈:执行函数时,函数内局部变量的存储单元可以在栈上创建,函数执行结果这些存储单元自动释放。栈内存分配运算符内置于处理器的指令集。
(3)堆:也称动态内存分配。用malloc()或new申请任意内存,程序员自己负责用free或delete释放内存。
28 struct和class区别
struct成员默认是公有的,而类的成员默认是私有的。在其他方面两者是相当的。
结构像一堆缺乏封装和功能开放的内存位,而类像活的并且可靠的社会成员,它有智能服务,有牢固的封装屏障和一个良好的定义接口。
29 一个类所占内存空间的问题
当一个类A中没有声明任何成员变量与成员函数时,sizeof(A)的值是否可以为0?
分析:肯定不是0.举个反例,如果是0的话,声明一个class A[10],而每一个对象占用的内存空间是0,这时就没有办法区分A[0],A[1]……。
30 8086汇编中,逻辑地址和物理地址如何转换?
为什么要特指在8086汇编下?因为C语言能编译成8086汇编语言。
通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到真正要访问的地址。
8086中含有存储器.存储器中每一个单元的地址可以用两种方法表示:
1).逻辑地址:其表达形式为“段地址:段内偏移地址”.
2).物理地址:CPU与存储器进行数据交换时在地址总线上提供的20位地址信息称为物理地址.
物理地址=段地址×10H+段内偏移量
CPU一次处理的数据是16位,地址总线实际上代表CPU的寻址能力,地址线为20条那么CPU实际的寻址能力就是2的20次方就是1M.实际的物理地址是这样形成的:段地址*10H+偏移地址,偏移地址用IP指向,IP是16位的.
例如段地址是1234H,偏移地址是4321H
那么实际的物理地址怎么算呢:1234H*10H+4321H=12340H+4321H=16661H
实际上可以这么来理解,就是段地址左移一位后加上偏移地址就得出实际的物理地址.
再比如:逻辑地址:1500H先化为2进制 0001 0101 0000 0000
左移四位就是0001 0401 0000 0000 0000,即15000H
若偏移地址为1000H 则物理地址就是16000H
逻辑地址与物理地址的转换
31 比较C++中的4种类型转换方式
(1) dynamic_cast,安全的向下转型,要确定一个对象是否是一个继承体系中的一个特定类型。它是唯一不能用旧风格语法执行的强制转型,也是唯一可能有重大运行时代价的强制转型。
(2)static_cast 可以用来强制隐性转换(non-const对象转型为const对象,int转型为double),也可以用于反向转换(void *指针转型为有类型指针,基类指针转型为派生类指针)。但是不能将一个const对象转型为non-const对象(只有const_cat可以做到)。
(3)const_cast 一般用于强制消除对象的常量性。唯一能做到这点的 C++风格的强制转型。
(4)reinterpret_cast特意用于底层的强制转型,导致实现依赖的结果,即不可移植的结果。
32 面向对象3个基本特征:
封装:将客观事物抽象成类,每个类对自身的数据和方法实现protection(private,protected,public)
继承:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。
多态:是将父对象设置成为一个或更多的它的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同方式运作。允许将子类类型的指针赋给父类类型的指针。
33 重载 和 重写
重载:允许存在多个同名函数,而这些函数的参数表不同(或许参数个数,参数类型不同,或两者都不同,)
重写:子类重新定义父类虚函数的方法。
重载:编译器根据函数不同的参数列表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。
function func(p:integer) : integer;
和
function func(p:string) : integer;
那么编译器做过修饰后的函数名称可能是这样的:int_func,str_func。对于这两个函数的调用,在编译期间就已经确定了是静态的,也就是说,它们的地址在编译期间就已经绑定了(早绑定)。因此,重载和多态没有关系。
重写:和多态相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。
34 多态作用
(1)隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用。
(2)接口重用。
35 ADO与ADO.NET区别
除了能够让应用程序处理存储于DBMS数据库中的数据这一基本相似点外,两者没有太多共同之处。但是ADO使用OLE DB接口并基于微软的COM技术,而ADO.NET拥有自己的ADO.NET接口并基于微软的.NET体系架构。
两者体系不同,接口不同,所以两者是不同的数据访问方式。ADO.NET提供对XML的支持。
36 new/delete、 malloc/free区别和联系
两者都是堆上进行动态的内存操作。使用malloc函数需要指定内存分配的字节数并且不能初始化对象,new会自动调用对象的构造函数。delete会调用对象的destructor,而free不会调用对象的destructor。
37 哪几种情况只能使用intialization list,而不能用assignment ?
当类中含有const和reference等成员变量时,基类的构造函数都需要初始化表。
有哪几种情况只能用intialization list 而不能用assignment
38 C++是否是类型安全的?
分析:不是。两个不同类型的指针之间可以强制转换。
39 main()函数执行前还会执行什么代码?
全局对象的构造函数会在main()函数之前执行。
40 static有什么用途?
静态变量的类型说明符是static。静态变量属于静态存储方式,但是属于静态存储方式的变量不一定就是静态变量。例如,外部变量虽属于静态存储方式,但是不一定静态变量,必须由static加以定义后才能成为静态外部变量,或称静态全局变量。
static的用途:限制变量的作用域、设置变量存储域。
41 实时系统基本特性
特性:在特定时间内完成特定任务、实时性与可靠性。
42 全局变量 vs.局部变量
全局变量存储在静态数据区,局部变量存储在堆栈中。
43 平衡二叉树
二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作左子树和右子树。
平衡二叉树的左右子树都是平衡二叉树,且左右子树的深度差值的绝对值不大于1.
43 定义int **a[3][4],则变量占用的内存空间是多少?
考虑系统为32位或是64位, 3*4*sizeof(a).
44 堆栈溢出原因?
堆栈溢出一般是循环的递归调用导致。如果使用的大数据结构的局部变量,也可能导致堆栈溢出。
没有回收垃圾资源会导致内存泄露最后耗尽系统内存。
45 什么函数不能声明为虚函数?
构造函数不能声明为虚函数。
(1) 所谓虚函数就是多态情况下只执行一个,从继承概念来讲,总是先构造父类对象,然后才能是子类对象。如果构造函数设为虚函数,那么当在构造父类的构造函数时就不得不显示的调用构造。还有一个原因是为了防错,若子类重写一个与父类构造函数一样的函数,那么父类构造函数将被覆盖,这样的话不能完成父类的构造也会出错。
(2)虚函数的主要意义在于被派生类继承从而产生多态。派生类的构造函数中,编译器会加入构造基类的代码,如果基类的构造函数用到参数,则派生类在其构造函数的初始化列表中必须为基类给出参数。
46 冒泡排序算法时间复杂度:O(N^2)
47 写出float x与零值比较的if语句 if(x>0.000001 && x<-0.000001)
分析:x是一个变量,被定义为float,所以在比较时,零值并不是0。
48 Internet采用哪种网络协议?结束主要层次结构?
Internet采用TCP/IP协议。
TCP/IP协议主要层次结构为5层:应用层、传输层、网络层、数据链路层和物理层。
49 Internet物理地址和IP地址转换采用什么协议?
采用ARP,地址解析协议。ARP协议的基本功能是通过目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。地址解析就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。
50 IP地址的编码分为哪两部分?
IP地址分为网络号和主机号。不过是要和“子网掩码”按位与之后,才能区分哪些是网络位,哪些是主机位。
《C/C++程序员面试宝典》 梁镇宇 P238~252