logo资料库

《C++面试宝典2015版》.docx

第1页 / 共83页
第2页 / 共83页
第3页 / 共83页
第4页 / 共83页
第5页 / 共83页
第6页 / 共83页
第7页 / 共83页
第8页 / 共83页
资料共83页,剩余部分请下载后查看
1.new、delete、malloc、free 关系 delete 会调用对象的析构函数,和 new 对应 free 只会释放内存,new 调用构造函数。malloc 与 free 是 C++/C 语言的标准库函数,new/delete 是 C++的运算符。它们都可用于申请动态 内存和释放内存。对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的 要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于 malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和 析构函数的任务强加于 malloc/free。因此 C++语言需要一个能完成动态内存分配和初始化 工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。注意 new/delete 不是库函数。 2.delete 与 delete []区别 delete 只会调用一次析构函数,而 delete[]会调用每一个成员的析构函数。在 More Effective C++中有更为详细的解释:“当 delete 操作符用于数组时,它为每个数组元素调用析构函数, 然后调用 operatordelete 来释放内存。”delete 与 New 配套,delete []与 new []配套 MemTest*mTest1=newMemTest[10]; MemTest*mTest2=newMemTest; int*pInt1=newint[10]; int*pInt2=newint; delete[]pInt1; //-1- delete[]pInt2; //-2- delete[]mTest1;//-3- delete[]mTest2;//-4- 在-4-处报错。 这就说明:对于内建简单数据类型,delete 和 delete[]功能是相同的。对于自定义的复杂数 据类型,delete 和 delete[]不能互用。delete[]删除一个数组,delete 删除一个指针简单来说, 用 new 分配的内存用 delete 删除用 new[]分配的内存用 delete[]删除 delete[]会调用数组元
素的析构函数。内部数据类型没有析构函数,所以问题不大。如果你在用 delete 时没用括 号,delete 就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。 3.C 和 C++ 的共同点?不同之处? 4.继承的优缺点。 类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。但 是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改 变从父类继承的实现。更糟的是,父类通常至少定义了子类的部分行为,父类的任何改变都 可能影响子类的行为。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他 更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。 (待补充) 5.C++有哪些性质(面向对象特点) 封装,继承和多态。 在面向对象程序设计语言中,封装是利用可重用成分构造软件系统的特性,它不仅支持系统 的可重用性,而且还有利于提高系统的可扩充性;消息传递可以实现发送一个通用的消息而 调用不同的方法;封装是实现信息隐蔽的一种技术,其目的是使类的定义和实现分离。 6.子类析构时要调用父类的析构函数 吗? 析构函数调用的次序是先派生类的析构后基类的析构,也就是说在基类的的析构调用的时候, 派生类的信息已经全部销毁了定义一个对象时先调用基类的构造函数、然后调用派生类的构 造函数;析构的时候恰好相反:先调用派生类的析构函数、然后调用基类的析构函数 JAVA 无析构函数深拷贝和浅拷贝 7.多态,虚函数,纯虚函数 多态:是对于不同对象接收相同消息时产生不同的动作。C++的多态性具体体现在运行和编 译两个方面:在程序运行时的多态性通过继承和虚函数来体现;
在程序编译时多态性体现在函数和运算符的重载上 虚函数:在基类中冠以关键字 virtual 的成员函数。 它提供了一种接口界面。允许在派生 类中对基类的虚函数重新定义。 纯虚函数的作用:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行 定义。作为接口而存在 纯虚函数不具备函数的功能,一般不能直接被调用。 从基类继承来的纯虚函数,在派生类中仍是虚函数。如果一个类中至少有一个纯虚函数,那 么这个类被称为抽象类(abstract class)。 抽象类中不仅包括纯虚函数,也可包括虚函数。l 抽象类必须用作派生其他类的基类,而不 能用于直接创建对象实例。但仍可使用指向抽象类的指针支持运行时多态性。 8.求下面函数的返回值(微软) int func(x) { int countx = 0; while(x) { countx ++; x = x&(x-1); } return countx; } 假定 x = 9999。 答案:8 思路:将 x 转化为 2 进制,看含有的 1 的个数。 9.什么是“引用”?申明和使用“引用”要 注意哪些问题? 答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。 申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个
名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用, 不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据 类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。 10.将“引用”作为函数参数有哪些特 点? (1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调 函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其 相应的目标对象(在主调函数中)的操作。 (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作; 而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量 是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据 较大时,用引用比用一般变量传递参数的效率和所占空间都好。 (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样 要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错 误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。 而引用更容易使用,更清晰。 11.在什么时候需要使用“常引用”? 如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使 用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名; 例 1 int a ; const int &ra=a; ra=1; //错误 a=1; //正确 例 2
string foo( ); void bar(string & s); 那么下面的表达式将是非法的: bar(foo( )); bar("hello world"); 原因在于 foo( )和"hello world"串都会产生一个临时对象,而在 C++中,这些临时对象都是 const 类型的。因此上面的表达式就是试图将一个 const 类型的对象转换为非 const 类型, 这是非法的。引用型参数应该在能被定义为 const 的情况下,尽量定义为 const 。 12.将“引用”作为函数返回值类型的格 式、好处和需要遵守的规则? 格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 } 好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变 量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生 runtime error! 注意事项: (1)不能返回局部变量的引用。这条可以参照 Effective C++[1]的 Item 31。主要原因是局 部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未 知状态。 (2)不能返回函数内部 new 分配的内存的引用。这条可以参照 Effective C++[1]的 Item 31。 虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部 new 分配内存的引 用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有 被赋予一个实际的变量,那么这个引用所指向的空间(由 new 分配)就无法释放,造成 memory leak。 (3)可以返回类成员的引用,但最好是 const。这条原则可以参照 Effective C++[1]的 Item 30。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常 常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。 如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏 业务规则的完整性。
(4)流操作符重载返回值申明为“引用”的作用: 流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl; 因 此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括: 返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝) 构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法 让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是 惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是 C++ 语言中引入引用这个概念的原因吧。 赋值操作符=。这个操作符象流操作符一样,是可以 连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便 可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。 例 3 #i nclude int &put(int n); int vals[10]; int error=-1; void main() { put(0)=10; //以 put(0)函数值作为左值,等价于 vals[0]=10; put(9)=20; //以 put(9)函数值作为左值,等价于 vals[9]=20; cout<=0 && n<=9 ) return vals[n]; else { cout<<"subscript error"; return error; } } (5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用, Effective C++[1]的Item23 详细的讨论了这个问题。主要原因是这四个操作符没有side effect, 因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部 变量的引用,返回一个 new 分配的对象的引用、返回一个静态对象引用。根据前面提到的
引用作为返回值的三个规则,第 2、3 两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为 true 而导致错误。所以可选的只剩下返回一个对象了。 13.“引用”与多态的关系? 引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的 派生类实例。例 4 Class A; Class B : Class A{...}; B b; A& ref = b; 14.“引用”与指针的区别是什么? 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程 序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此 外,就是上面提到的对函数传 ref 和 pointer 的区别。 15.什么时候需要“引用”? 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其 它情况都推荐使用引用。以上 2-8 参考:http://develop.csai.cn/c/NO0000021.htm 16.结构与联合有和区别? (1). 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放 了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员 的存放地址不同)。 (2). 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结 构的不同成员赋值是互不影响的。 17.面关于“联合”的题目的输出? a) #i nclude union
{ int i; char x[2]; }a; void main() { a.x[0] = 10; a.x[1] = 1; printf("%d",a.i); } 答案:266 (低位低地址,高位高地址,内存占用情况是 Ox010A) b) main() { union{ /*定义一个联合*/ int i; struct{ /*在联合中定义一个结构*/ char first; char second; }half; }number; number.i=0x4241; /*联合成员赋值*/ printf("%c%cn", number.half.first, mumber.half.second); number.half.first='a'; /*联合中结构成员赋值*/ number.half.second='b'; printf("%xn", number.i); getch(); } 答案: AB (0x41 对应'A',是低位;Ox42 对应'B',是高位) 6261 (number.i 和 number.half 共用一块地址空间)
分享到:
收藏