C经典笔试题附答案Word下载.doc
《C经典笔试题附答案Word下载.doc》由会员分享,可在线阅读,更多相关《C经典笔试题附答案Word下载.doc(22页珍藏版)》请在冰豆网上搜索。
//加成员名限定A:
:
但更好的办法是在类C中也定义一个同名print函数,根据需要调用A:
print()还是B:
print(),从而实现对基类同名函数的隐藏
9:
下列公共基类导致的二义性如何解决?
classA{ //公共基类
//public成员列表
voidprint(){
cout<
"
thisisxinA:
<
classB:
publicA{};
classC:
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:
若改为“d.A:
”又会如何呢?
由于d对象中有两个A类对象,故编译会报“基类A不明确”。
2)语句“A*pa=(A*)&
”产生的二义性是由于d中含有两个基类对象A,隐式 转换时不知道让pa指向哪个子对象,从而出错。
可改为以下的一种:
A*pa=(A*)(B*)&
//上行转换
A*pa=(A*)(C*)&
//上行转换
事实上,使用关键字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();
”,则“a=b;
3)A实现了非explicit的参数为B(可以有其他带默认值的参数)的构造函 数
classA{
A(constB&
11:
调用一成员函数时,使用动态联编的情况是()。
(2011•淘宝)
A.通过对象调用一虚函数 B.通过指针或引用调用一虚函数
C.通过对象调用静态函数 D.通过指针或引用调用一静态函数
结合一段示例代码来看虚函数的作用,以帮助大家理解多态的意义所在。
例2:
下述代码的输出结果是什么?
classbase{
virtualvoiddisp(){cout<
hello,base1"
}voiddisp2(){cout<
hello,base2"
endl;
classchild1:
publicbase{
voiddisp(){cout<
hello,child1"
}
voiddisp2(){cout<
hello,child2"
base*base=NULL;
child1objchild1;
base=&
objchildl;
base->
disp();
disp2();
输出:
hello,childl
hello,base2
从上述代码可见,通过指针访问函数时:
1)不加virtual时,具体调用哪个版本的函数只取决于指针本身的类型,和指针所指对象的类型无关。
2)而加virtual时,具体调用哪个版本的函数不再取决于指针本身的类型,而是取决于指针所指对象的类型。
13:
构造函数为什么不能为虚函数?
假设有如下代码:
classA{
A(){}
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{
B(){
cout<
”Bconstructor,”;
s=“B”;
voidf(){cout<
s;
peivate:
strings;
classD:
publicB{
D():
B(){
Dconstructor,"
;
s=“D”;
}private:
intmain(void){
B*b=newD();
b->
f();
((D*)b)->
f();
deleteb;
return0;
输出结果是:
Bconstructor,Dconstructor,BD
若在类B中的函数f前加上virtual关键字,则输出结果为:
Bconstructor,Dconstructor,DD
可见若函数不是虚函数,则不是动态绑定。
17:
下列代码的输出结果是什么?
(2012•网易)
classA{
virtualvoidFun(intnumber=10){
std:
A:
Funwithnumber"
number<
publicA{
virtualvoidFun(intnumber=20){
cout<
”B:
Funwithnumber’’<
A&
a=b;
a.Fun();
B:
Funwithnumber10。
虚函数动态绑定到B,但缺省实参是编译时候 确定的10,而非20。
构造函数和析构函数中的虚函数
构造派生类对象时,首先运行基类构造函数初始化对象的基类部分。
在执行基类构造函数时,对象的派生类部分是未初始化的。
实际上,此时对象还不是一个派生类对象。
撤销派生类对象时,首先撤销它的派生类部分,然后按照与构造顺序的逆序撤销它的基类部分。
在这两种情况下,运行构造函数或析构函数时,对象都是不完整的。
为了适应这种不完整,编译器将对象的类型视为在构造或析构期间发生了变化。
在基类构造函数或析构函数中,将派生类对象当作基类型对象对待。
如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。
18:
以下哪些做法是不正确或者应该极力避免的()。
(多选)(2012•搜狗)
A.构造函数声明为虚函数 B.派生关系中的基类析构函数声明为虚函数
C.构造函数调用虚函数 D.析构函数调用虚函数
ACD。
构造函数和析构函数是特殊的成员函数,在其中访问虚函数时,C++采用静态联编,即在构造函数或析构函数内,即使是使用虚函数名”的形式来调用,编译器仍将其解释为静态联编的“本类名:
虚函数名”,因而这样会与使用者的意图不符,应该尽量避免。
9.2.2虚函数表指针(vptr)及虚基类表指针(bptr)
C++在布局以及存取时间上主要的额外负担是由virtual引起的,包括:
virtual