logo资料库

招银网络面试.docx

第1页 / 共10页
第2页 / 共10页
第3页 / 共10页
第4页 / 共10页
第5页 / 共10页
第6页 / 共10页
第7页 / 共10页
第8页 / 共10页
资料共10页,剩余部分请下载后查看
1 拷贝构造函数为什么使用引用实现? 答:如果不采用引用实现,即采用传值调用,对于拷贝构造函数,将是以下形式,MyClass(const MyClass rhl);对于 MyClass a=b 这种形式,将会调用 a.MyClass(b);所以这时候又会去调用 const MyClass rhl=b,即等价于 rhl.MyClass(b);这个时候还会去调用拷贝构造函数。所以, 如果不采用引用会导致无限递归调用。 2 静态成员函数不能声明为虚函数。 答:静态成员函数编译时绑定,虚函数运行时绑定。静态成员函数是属于类的,而不是属于 某一对象,不会传入隐式 this 指针;虚函数是特殊的成员函数,用来实现运行时多态,没有 this 指针无法实现虚函数调用。 3 new 和 malloc 的区别 New 返回指定类型的指针,并可以自动计算所需大小;而 malloc 则必须由程序员计算字节 数,并且在返回后强制转化为实际类型的指针。 Malloc 只管分配内存,并不能对内存进行初始化,所以得到的一片新内存中,里面的值将是 随机的,而 new 在分配内存的时候可以进行初始化。 除了分配以及最后释放以外,其余的操作基本一样。 Malloc 和 free 成对使用,new 和 delete 成对使用;malloc 和 free 是 C++/c 里面的库函数, 而 new 和 delete 是 C++里面的运算符。 对非内部数据类型而言,malloc 无法满足要求。 C++程序经常调用 C,而 C 智能使用 malloc/free 管理内存。 4 虚函数怎么实现的 编译器处理虚函数的原理方法是:给每个对象添加一个隐藏成员,该隐藏成员保存了一个指 向函数地址数组的指针,这个数组称为虚函数表,该表里面保存了虚函数的地址;调用虚函 数时,程序将查看该对象的表地址,然后转向相应的函数地址表,如果使用类声明定义中的 第一个虚函数,则程序将使用数组中的第一个函数地址,并执行具有该地址的函数…。 使用虚函数包括一定的成本: 每个对象都将增大,增大为存储地址的空间; 对于每个类,编译器会创建出虚函数地址表; 对于虚函数调用,需要到表中查找地址,会增加额外的操作。 5 多态和继承 继承:可以使用现有类的所有功能,并且无需在编写原有类的情况下对这些功能进行扩展, 新创建的类叫做子类或者派生类,被继承的类叫做基类或者父类。父类更通用,子类更具体; 可以有效的实现代码复用,避免重复代码的出现。 继承概念的实现方式有三类:实现继承、接口继承和可视继承。 实现继承是指使用基类的属性和方法而无需额外编码的能力; 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力; 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。 多态:同一个实现接口,使用不同的实例而指向不同的操作。 多态是指允许将父类对象设置成和他的子类相等的技术,赋值之后,父对象就可以根据他的
子对象的特性以不同的方式运作,简单而言,允许将子类型的指针赋值给父类型的指针。 实现多态,有二种方式,覆盖,重载。 覆盖,是指子类重新定义父类的虚函数的做法。 重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参 数类型不同,或许两者都不同)。实际上不属于多态。 封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的 目的都是为了——代码重用;而多态则是为了实现另一个目的——接口重用! 6 冒泡排序及其优化 每一趟排序比较两个相邻的数字,将小数放在前面,大数放在后面,直到最后只剩一个元素。 优化 1:设置一个标志位来判断是否发生了交换,若在一趟中,没有两个相邻元素交换,则 提前结束; 优化 2:每趟排序记录最后发生交换的位置,作为下一趟计较结束的位置。 7 堆排序的思想 堆排序的特点是将数组看成是完全二叉树的顺序存储结构,利用完全二叉树的双亲结点和孩 子结点的内在关系,选择当前无序序列里面最小或者最大的元素。 8 排序算法有哪些 A 插入排序算法 直接插入排序:最好时间:O(n), 平均时间:O(n 平方),空间:O(1),稳定。 平均时间:O(n)平方,空间:O(1),稳定。 折半插入排序: 平均时间:O(n1.3 次方),空间:O(1),不稳定。 希尔 排序: B 交换排序 冒泡 排序 :最好时间:O(n) , 平均时间:O(n 平方),空间:O(1),稳定。 快速 排序 :最坏时间:O(n)平方 平均时间:O(nlogn),空间:O(log(n)),不稳定。 C 选择排序 简单选择排序: 堆 排序: D 归并排序 平均时间:O(n 平方),空间 O(1),不稳定。 平均时间:O(nlogn),空间 O(1),不稳定。 平均时间:O(nlogn),空间 O(n),稳定。 9 char a[]和 char *b 的区别  char *a = "abcd"; 此时"abcd"存放在常量区。通过指针只可以访问字符串常量,而不可 以改变它。  而 char a[20] = "abcd"; 此时 "abcd"存放在栈。可以通过指针去访问和修改数组内容。 char *a = "abcd"; 是在编译时就确定了(因为为常量)。   而 char a[20] = "abcd"; 在运行时确定 10 内存分配方式 内存分配有三种:静态存储区、堆区和栈区。他们的功能不同,对他们使用方式也就不同。 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。
它主要存放静态数据、全局数据和常量。 栈区:在执行函数时,函数(包括 main 函数)内局部变量的存储单元都可以在栈上创建, 函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率 很高,但是分配的内存容量有限。(任何变量都处于站区,例如 int a[] = {1, 2},变量 a 处于栈 区。数组的内容也存在于栈区。) 堆区:亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序 员自己负责在适当的时候用 free 或 delete 释放内存。动态内存的生存期可以由我们决定, 如果我们不释放内存,程序将在最后才释放掉动态内存。 但是,良好的编程习惯是:如果 某动态内存不再使用,需要将其释放掉,并立即将指针置位 NULL,防止产生野指针。 11 循环队列的实现 12 虚析构函数的作用 13 什么是死锁,必要条件,解决办法 所谓死锁,是指多个进程循环等待它方占有的资源而无限期地僵持下去的局面。很显然,如 果没有外力的作用,那麽死锁涉及到的各个进程都将永远处于封锁状态; 互斥条件; 不可抢占条件; 占有且申请条件; 循环等待条件. 14 内连接、外连接、交叉链接 使用比较运算符(包括=、>、<、<>、>=、<=、!>和!<)进行表间的比 较操作,查询与连接条件相匹配的数据。根据比较运算符不同,内连接 分为等值连接和不等连接两种; 内连接:保留关系表中所有匹配的数据,舍弃不匹配的数据记录; 外连接:不仅保留关系表中所有匹配的数据,还会保留部分不匹配的数 据记录; 全连接:笛卡尔积。 15 sleep 和 wait 的区别 16 索引以及其好处与坏处、存储过程以及函数的概念 索引是提高 select 操作性能的最佳途径,分为 Btree 索引和 Hash;
Btree 和 Hash 索引的区别:Btree 支持范围内的查询,而 Hash 只支 持精确的查找,即等号查询。 存储过程(Stored Producedures)是一组为完整特定功能的 SQL 语句集,经编译后存储在 数据库中。用户通过指定存储过程的名字给出参数(如果该存储过程带有参数)来执行它。 本质上没区别。只是函数有限制只能返回一个标量,而存储过程可以返回多个。并且函数是 可以嵌入在 SQL 中使用的,可以在 SELECT 等 SQL 语句中调用,而存储过程不行。 17 视图和表的区别 是一种虚拟的表结构,主要是简单,透明,用来加强底层数据库的安全 性。 18 内存分区、堆、栈 19 堆栈区别,从内存和数据结构两方面看 20 TCP/IP 的四层,几个协议分别 21 进程线程的区别 22 线程同步机制 23 数据库索引(索引是不是立即生效) 24 数据量很大,怎么优化数据库提高速度 25 跳跃表 26 垃圾回收机制 27 事物怎么实现的,性能如何 28 提高项目的并发性能 29 最近读了什么书 30 优缺点 31 malloc alloc calloc 32 大小端模式 33 strlen 和 sizeof 的区别
34 stl 35 map 和 hash_map map 是接口,不能用 new 出对象 hashmap 是继承 map 接口的实现类,可以 new 出对象 36 面向对象 37 list 和 vector 38 分页 39 where 和 having Where 过滤指定的行而不是分组,having 过滤分组; Where 在数据分组前进行过滤,having 在分组之后进行过滤; 40 select—from—where—group by—having—order by。 41 在 C++程序中调用被 C 编译器编译后的函数,为什么要加 extern "C"? 答案:C++语言支持函数重载,C 语言不支持函数重载。 函数被 C++编译后在库中的名字与 C 语言的不同。 假设某个函数的原型为 void foo(int x, int y)。 该函数被 C 编译器编译后在库 中的名字为_foo,而 C++编译器则会产生像_foo_int_int 之类的名字。C++提供了 C 连接交换 指定符号 extern "C"解决名字匹配问题. 42 const 与#define 相比有什么不同? 答案:C++语言可以用 const 定义常量,也可以用#define 定义常量,但是前者比后者有更 多的优点: ● const 常量有数据类型,而宏常量没有数据类型。 编译器可以对前者进行类型安全检查, 而对后者只进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意料不到 的错误(边际效应)。 ● 有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。 在 C++程序中只使用 const 常量而不使用宏常量,即 const 常量完全取代宏常量。 43 通过对 sizeof 与 strlen 的深入理解,得出两者区别如下. (1)sizeof 操作符的结果类型是 size_t,它在头文件中的 typedef 为 unsigned int 类型。 该 类型保证能容纳实现所建立的最大对象的字节大小。 (2)sizeof 是运算符,strlen 是函数。 (3)sizeof 可以用类型做参数,strlen 只能用 char*做参数,且必须是以“\0”结尾的。sizeof 还可以用函数做参数. (4)大部分编译程序在编译的时候就把 sizeof 计算过了,是类型或是变量的长度。 这就是 sizeof(x)可以用来定义数组维数的原因. (5)strlen 的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型 占内存的大小。 (6)sizeof 后如果是类型必须加括号,如果是变量名可以不加括号。 这是因为 sizeof 是 个操作符而不是个函数。 (7)当使用了一个结构类型或变量时,sizeof 返回实际的大小。 当使用一静态的空间数
组时,sizeof 返回全部数组的尺寸。 sizeof 操作符不能返回被动态分配的数组或外部的数组 的尺寸。 (8)数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如 fun(char [8])、 fun(char [])都等价于 fun(char *)。 在 C++里传递数组永远都是传递指向数 组首元素的指针,编译器不知道数组的大小。 如果想在函数内知道数组的大小,需要这样 做:进入函数后用 memcpy 将数组复制出来,长度由另一个形参传进去。 44 内联函数和宏的差别是什么? 内联函数和普通函数相比可以加快程序运行的速度,因为不需要中断调用,在编译的时候, 内联函数可以直接被镶嵌到目标代码中。 而宏只是一个简单的替换。 内联函数要做参数类型检查,这是内联函数跟宏相比的优势。 inline 是指嵌入代码,就是在调用函数的地方不是跳转,而是把代码直接写到那里去。对于 短小的代码来说 inline 增加空间消耗换来的是效率提高,这方面和宏是一模一样的,但是 inline 在和宏相比没有付出任何额外代价的情况下更安全。 至于是否需要 inline 函数,就需 要根据实际情况来取舍了。 inline 一般只用于如下情况: (1)一个函数不断被重复调用。 (2)函数只有简单的几行,且函数内不包含 for、 while、 switch 语句。 一般来说,我们写小程序没有必要定义成 inline,但是如果要完成一个工程项目,当一个简 单函数被调用多次时,则应该考虑用 inline。 宏在 C 语言里极其重要,而在 C++里用得就少多了。 宏是在代码处不加任何验证的简单替代,而内联函数是将代码直接插入调用处,而减少了普 通函数调用时的资源消耗。 宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。 关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前 面不起任何作用。 45 指针和引用的差别? (1)非空区别。 在任何情况下都不能使用指向空值的引用。 一个引用必须总是指向某 些对象。 因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不 指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。 相反, 如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引 用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。 (2)合法性区别。 在使用引用之前不需要测试它的合法性。 相反,指针则应该总是被 测试,防止其为空。 (3)可修改区别。 指针与引用的另一个重要的区别是指针可以被重新赋值以指向另一 个不同的对象。 但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定 的对象其内容可以改变。 (4)应用区别。 总的来说,在以下情况下应该使用指针:一是考虑到存在不指向任何对象 的可能(在这种情况下,能够设置指针为空),二是需要能够在不同的时刻指向不同的 对象(在这种情况下,你能改变指针的指向)。 如果总是指向一个对象并且一旦指向一个对 象后就不会改变指向,那么应该使用引用。 46 C++中有了 malloc/free,为什么还需要 new/delete?
malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。 它们都可用于申 请动态内存和释放内存。 对于非内部数据类型的对象而言,只用 malloc/free 无法满足动态对象的要求。 对象在创建 的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。 由于 malloc/free 是库 函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强 加于 malloc/free。 因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清 理与释放内存工作的运算符 delete。 new/delete 不是库函数,而是运算符。 47 this 指针易混的几个问题如下。 (1)This 指针本质是一个函数参数,只是编译器隐藏起形式的,语法层面上的参数。this 只能在成员函数中使用,全局函数、 静态函数都不能使用 this。实际上,成员函数默认第 一个参数为 T* const this。 (2)this 在成员函数的开始前构造,在成员的结束后清除。 这个生命周期同任何一个函 数的参数是一样的,没有任何区别。 当调用一个类的成员函数时,编译器将类的指针作为 函数的 this 参数传递进去。 (3)this 指针并不占用对象的空间。 this 相当于非静态成员函数的一个隐函的参数,不占对象的空间。 它跟对象之间没有包 含关系,只是当前调用函数的对象被它指向而已。所有成员函数的参数,不管是不是隐含的, 都不会占用对象的空间,只会占用参数传递时的栈空间,或者直接占用一个寄存器。 (4)this 指针是什么时候创建的? this 在成员函数的开始执行前构造,在成员的执行结束后清除。 (5)this 指针存放在何处?堆、 栈、 还是其他? this 指针会因编译器不同而有不同的放置位置。 可能是堆、 栈,也可能是寄存器。 C++是一种静态的语言,那么对 C++的分析应该从语法层面和实现层面两个方面进行。语法 上,this 是个指向对象的“常指针”,因此无法改变。 它是一个指向相应对象的指针。 所有 对象共用的成员函数利用这个指针区别不同变量,也就是说,this 是“不同对象共享相同成 员函数”的保证。 (6)我们只有获得一个对象后,才能通过对象使用 this 指针。 如果我们知道一个对象 this 指针的位置,可以直接使用吗? this 指针只有在成员函数中才有定义。 因此,你获得一个对象后,也不能通过对象使用 this 指针。 所以,我们无法知道一个对象的 this 指针的位置(只有在成员函数里才有 this 指针的位置)。 当然,在成员函数里,你是可以知道 this 指针的位置的(可以通过&this 获 得), 也可以直接使用它。 48 STL 有以下优点: ● 可以方便、 容易地实现搜索数据或对数据排序等一系列的算法。 ● 调试程序时更加安全和方便。 ● 即使是人们用 STL 在 UNIX 平台下写的代码,也可以很容易地理解(因为 STL 是跨平台的) 49 介绍一下 STL 和包容器,如何实现?举例实现 vector。 答案:C++的一个新特性就是采用了标准模板库(STL)。 所有主要编译器销售商现在都把标 准模板库作为编译器的一部分进行提供。 标准模板库是一个基于模板的容器类库,包括链
表、 列表、 队列和堆栈。 标准模板库还包含许多常用的算法,包括排序和查找。 标准模板库的目的是提供对常用需求重新开发的一种替代方法。 标准模板库已经经过测试 和调试,具有很高的性能并且是免费的。 最重要的是,标准模板库是可重用的。 当你知道 如何使用一个标准模板库的容器以后,就可以在所有的程序中使用它而不需要重新开发了。 容器是包容其他对象的对象。 标准 C++库提供了一系列的容器类,它们都是强有力的工具, 可以帮助 C++开发人员处理一些常见的编程任务。 标准模板库容器类有两种类型,分别为 顺序和关联。 顺序容器可以提供对其成员的顺序访问和随机访问。 关联容器则经过优化关 键值访问它们的元素。 标准模板库在不同操作系统间是可移植的。 所有标准模板库容器类 都在 namespace std. 50 51 什么是封装? 从字面意思来看,封装就是把一些相关的东西打包成一“坨”。 封装最广为人知的例子,就 是在面向对象编程里面,把数据和针对该数据的操作,统一到一个 class 里。 很多人把封装的概念局限于类,认为只有 OO 中的 class 才算是封装。 这实际上是片面的, 在很多不使用“类”的场合,一样能采用封装的手法: 1>通过文件。 比如 C 和 C++支持对头文件的包含(#include)。 因此,可以把一些相关的常量定义、 类 型定义、 函数声明,统统封装到某个头文件中。 2>通过 namespace/package/module。 C++的 namespace、 Java 的 package、 Python 的 module,这些语法虽然称呼各不相同,但 具有相同的本质。 因此,也可以利用这些语法来进行封装。 那么封装有一个主要的好处,就是增加软件代码的内聚性。 通过增加内聚性,进而提高可 复用性和可维护性。 此外还可以“信息隐藏”:把不该暴露的信息藏起来。 如 private、 protected 之类的关键字。 这些关键字可以通过访问控制,来达到信息隐藏的目的。 52 析构函数可以为 virtual 型,构造函数则不能。 那么为什么构造函数不能为虚?
分享到:
收藏