C经典笔试题附答案.doc
- 文档编号:230158
- 上传时间:2022-10-07
- 格式:DOC
- 页数:22
- 大小:232KB
C经典笔试题附答案.doc
《C经典笔试题附答案.doc》由会员分享,可在线阅读,更多相关《C经典笔试题附答案.doc(22页珍藏版)》请在冰豆网上搜索。
8:
下列多重继承时的二义性问题如何解决?
classA{ //类A的定义
public:
voidprint(){cout<<"Hello,thisisA"< }; classB{ //类B的定义 public: voidprint(){cout<<"Hello,thisisB"< }; classC: publicA,publicB{ //类C由类A和类B共同派生而来 public: voiddisp(){print();}//编译器无法决定采用A类中定义的版本还是B类中的版本 }; 解答: 若两个基类中具有同名的数据成员或成员函数,应使用成员名限定来消除二义性,如: voiddisp(){ A: : print(); //加成员名限定A: : } 但更好的办法是在类C中也定义一个同名print函数,根据需要调用A: : print()还是B: : print(),从而实现对基类同名函数的隐藏 9: 下列公共基类导致的二义性如何解决? classA{ //公共基类 public: //public成员列表 voidprint(){ cout<<"thisisxinA: "< classB: publicA{}; classC: publicA{}; classD: publicB,publicC{}; voidmain(){ Dd; //声明一个D类对象d A*pa=(A*)&d; //上行转换产生二义性 d.print(); //print()具有二义性,系统不知道是调用B类的还是C类的print()函数 } 注意: 把子类的指针或引用转换成基类指针或引用是上行转换,把基类指针或引用转换成子类指针或引用是下行转换。 解答: 1)main函数中语句“d.print();”编译错误,可改为以下的一种: d.B: : print();d.C: : print(); 若改为“d.A: : print();”又会如何呢? 由于d对象中有两个A类对象,故编译会报“基类A不明确”。 2)语句“A*pa=(A*)&d;”产生的二义性是由于d中含有两个基类对象A,隐式 转换时不知道让pa指向哪个子对象,从而出错。 可改为以下的一种: A*pa=(A*)(B*)&d;//上行转换 A*pa=(A*)(C*)&d;//上行转换 事实上,使用关键字virtual将共同基类A声明为虚基类,可有效解决上述 问题。 10: 下面哪种情况下,B不能隐式转换为A( )? (2011•腾讯) A.classB: publicA{} B.classA: publicB{} C.classB{operatorA();} D.classA{A(constB&);} 解答: B。 因为子类包含了父类部分,所以子类可以转换为父类,但是相反, 父类没有子类额外定义的部分,所以不能转换为子类,故A正确,而B错误。 非C++内建型别A和B,在以下几种情况下B能隐式转化为A。 1)B公有继承自A,可以是间接继承的。 classB: publicA{ }; 此时若有“Aa;Bb;”,则“a=b;”合法。 2)B中有类型转换函数。 classB{ operatorA(); }; 此时若有“Aa;Bb;”,则“a=b;”合法。 3)A实现了非explicit的参数为B(可以有其他带默认值的参数)的构造函 数 classA{ A(constB&); }; 此时若有“Aa;Bb;”,则“a=b;”合法。 11: 调用一成员函数时,使用动态联编的情况是()。 (2011•淘宝) A.通过对象调用一虚函数 B.通过指针或引用调用一虚函数 C.通过对象调用静态函数 D.通过指针或引用调用一静态函数 解答: B。 结合一段示例代码来看虚函数的作用,以帮助大家理解多态的意义所在。 例2: 下述代码的输出结果是什么? classbase{ public: virtualvoiddisp(){cout<<"hello,base1"< }; classchild1: publicbase{ public: voiddisp(){cout<<"hello,child1"< voiddisp2(){cout<<"hello,child2"< voidmain(){ base*base=NULL; child1objchild1; base=&objchildl; base->disp(); base->disp2(); 解答: 输出: hello,childl hello,base2 从上述代码可见,通过指针访问函数时: 1)不加virtual时,具体调用哪个版本的函数只取决于指针本身的类型,和指针所指对象的类型无关。 2)而加virtual时,具体调用哪个版本的函数不再取决于指针本身的类型,而是取决于指针所指对象的类型。 13: 构造函数为什么不能为虚函数? 解答: 假设有如下代码: classA{ A(){} }; classB: publicA{ B(): A() {} }; intmain(){ Bb; B*pb=&b; } 则构造B类的对象时: 1.根据继承的性质,构造函数执行顺序是: A()B() 2.根据虚函数的性质,如果A的构造函数为虚函数,且B类也给出了构造函数,则应该只执行B类的构造函数,不再执行A类的构造函数。 这样A就不能构造了。 3.这样1和2就发生了矛盾。 另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。 14: 哪些函数不能为虚函数? 解答: 常见的不能声明为虚函数的有: 普通函数(非成员函数)、静态成员函数、构造函数、友元函数,而内联成员函数、赋值操作符重载函数即使声明为虚函数也无意义。 1)为什么C++不支持普通函数为虚函数? 普通函数(非成员函数)只能被overload(重载),不能被override(覆盖),声明为虚函数也没有什么意义,因此编译器会在编译时绑定函数。 为什么C++不支持构造函数为虚函数? 上例己经给出了答案。 2)为什么C++不支持静态成员函数为虚函数? 静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码, 它不归某个具体对象所有,所以它没有要动态绑定的必要性。 3)为什么C++不支持友元函数为虚函数? 因为C++不支持友元函数的继承,没有实现为虚函数的必要。 以下两种函数被声明为虚函数时,虽然编译器不会报错,但是毫无意义。 内联函数: 内联函数是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后,对象能够准确地执行自己的动作,这是不可能统一的。 即使虚函数被声明为内联函数,编译器遇到这种情况根本不会把这样的函数内联展开,而是当作普通函数来处理。 赋值运算符: 虽然可以在基类中将成员函数operator定义为虚函数,但这样做没有意义。 赋值操作符重载函数要求形参与类本身类型相同,故基类中的赋值操作符形参类型为基类类型,即使声明为虚函数,也不能作为子类的赋值操作符。 15: 以下描述正确的是()。 (2011•盛大游戏) A.虚函数是可以内联的,可以减少函数调用的开销提高效率 B.类里面可以同时存在函数名和参数都一样的虚函数和静态函数 C.父类的析构函数是非虚的,但是子类的析构函数是虚的,delete子类对象指针会调用父类的析构函数 D•以上都不对 解答: C。 C中delete子类对象指针会调用父类的析构函数(即使子类的析构 函数不是虚的,对子类对象指针调用析构函数,也会调用父类的析构函数), 但若delete父类对象指针却不会调用子类的析构函数(因为父类的析构函数 不是虚函数,不执行动态绑定)。 16: 以下代码的输出结果是()。 (2012•小米) classB{ public: B(){ cout<<”Bconstructor,”; s=“B”; } voidf(){cout< peivate: strings; }; classD: publicB{ public: D(): B(){ cout<<"Dconstructor,"; s=“D”; } voidf(){cout< strings; }; intmain(void){ B*b=newD(); b->f(); ((D*)b)->f(); deleteb; return0; } 解答: 输出结果是: Bconstructor,Dconstructor,BD 若在类B中的函数f前加上virtual关键字,则输出结果为: Bconstructor,Dconstructor,DD 可见若函数不是虚函数,则不是动态绑定。 17: 下列代码的输出结果是什么? (2012•网易) classA{ public: virtualvoidFun(intnumber=10){ std: : cout<<"A: : Funwithnumber"< } }; classB: publicA{ public: virtualvoidFun(intnumber=20){ std: : cout<<”B: : Funwithnumber’’< } }; intmain(){ Bb; A&a=b; a.Fun(); return0; } 解答: B: : Funwithnumber10。 虚函数动态绑定到B,但缺省实参是编译时候 确定的10,而非20。 构造函数和析构函数中的虚函数 构造派生类对象时,首先运行基类构造函数初始化对象的基类部分。 在执行基类构造函数时,对象的派生类部分是未初始化的。 实际上,此时对象还不是一个派生类对象。 撤销派生类对象时,首先撤销它的派生类部分,然后按照与构造顺序的逆序撤销它的基类部分。 在这两种情况下,运行构造函数或析构函数时,对象都是不完整的。 为了适应这种不完整,编译器将对象的类型视为在构造或析构期间发生了变化。 在基类构造函数或析构函数中,将派生类对象当作基类型对象对待。 如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。 18: 以下哪些做法是不正确或者应该极力避免的()。 (多选)(2012•搜狗) A.构造函数声明为虚函数 B.派生关系中的基类析构函数声明为虚函数 C.构造函数调用虚函数 D.析构函数调用虚函数 解答: ACD。 构造函数和析构函数是特殊的成员函数,在其中访问虚函数时,C++采用静态联编,即在构造函数或析构函数内,即使是使用虚函数名”的形式来调用,编译器仍将其解释为静态联编的“本类名: : 虚函数名”,因而这样会与使用者的意图不符,应该尽量避免。 9.2.2虚函数表指针(vptr)及虚基类表指针(bptr) C++在布局以及存取时间上主要的额外负担是由virtual引起的,包括: virtual
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 经典 笔试 答案