C++经典笔试题附答案.docx

上传人:b****5 文档编号:30743984 上传时间:2023-08-20 格式:DOCX 页数:24 大小:80.91KB
下载 相关 举报
C++经典笔试题附答案.docx_第1页
第1页 / 共24页
C++经典笔试题附答案.docx_第2页
第2页 / 共24页
C++经典笔试题附答案.docx_第3页
第3页 / 共24页
C++经典笔试题附答案.docx_第4页
第4页 / 共24页
C++经典笔试题附答案.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

C++经典笔试题附答案.docx

《C++经典笔试题附答案.docx》由会员分享,可在线阅读,更多相关《C++经典笔试题附答案.docx(24页珍藏版)》请在冰豆网上搜索。

C++经典笔试题附答案.docx

C++经典笔试题附答案

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)

2)

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++不支持普通函数为虚函数?

,声明为虚函数也没有什么意义,因此编译器会在编译时绑定函数。

为什么C++不支持构造函数为虚函数?

上例己经给出了答案。

2)为什么C++不支持静态成员函数为虚函数?

3)为什么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引起的,包括:

virtualfunction机制:

用以支持一个有效率的“执行期绑定”;

virtualbaseclass:

用以实现多次出现在继承体系中的基类,有一个单一而被共享的实体。

19:

一般情况下,下面哪些操作会执行失败?

()(多选)(2012•搜狗)

classA{

public:

stringa;

voidfl(){printf("HelloWorld");}voidf2(){

a="HelloWorld";printf("%s",a.c_str());

}

virtualvoidf3(){printf("HelloWorld");}virtualvoidf4(){

a="HelloWorld";

printf("%s",a.c_str());}

};

A.A*aptr=NULL;aptr->f1();

B.A*aptr=NULL;aptr->f2();

C.A*aptr=NULL;aptr->f3();

D.A*aptr=NULL;aptr->f4();

解答:

BCD。

因为A没有使用任何成员变量,且fl函数是非虚函数(不存在于具体

对象中),是静态绑定的,所以A不需要使用对象的信息,故正确。

在B中使用了成员变量,而成员变量只能存在于对象中:

C中f3是虚函数,需要使用虚表指针(存在于具体对象中);

D同C。

可见BCD都需要有具体存在的对象,故不正确。

20:

请问下面代码的输出结果是什么?

classA{

public:

A(){a=l;b=2;}

private:

inta;intb;

};

classB{

public:

B(){c=3;}

voidprint(){cout<

private:

intc;

};

intmain(intargc,char*argv[]){

Aa;

B*pb=(B*)(&a);

pb->print();

return0;

}

解答:

1。

这里将一个指向B类型的指针指向A类型的对象,由于函数print并不位于对象中,且print是非虚函数,故执行静态绑定(若是动态绑定,则需要virtual的信息,而对象a中不存在virtual信息,则执行会出错)。

当调用print函数时,需要输出c的值,程序并不知道指针pb指向的对象不是B类型的对象,只是盲目地按照偏移值去取,c在类B的对象中的偏移值跟a在类A的对象中的偏移值相等(都位于对象的起始地址处),故取到a的值1。

21:

sizeof(Test)=4?

sizeof(s)=4?

sizeof(testl)=1?

classTest{inta;staticdoublec;}

Test*s;

classtest1{};

解答:

sizeof(Test)=4,因为。

sizeof(s)=4,因为s为一个指针。

sizeof(testl)=l,因为空类大小为1。

22:

下列表达式在32位机器编译环境下的值为()。

(2012•海康威视)

classA{};

classB{

public:

B();

virtual〜B();

};

classC{

private:

#pragmapack(4)

inti;shortj;floatk;char1[64];longm;char*p;

#pragmapack()

};

classD{

private:

#pragmapack

(1)

inti;shortj;floatk;char1[64];longm;char*p;

#pragmapack()

};

intmain(void){

printf("%d\n",sizeof(A));

printf("%d\n",sizeof(B));

printf("%d\n",sizeof(C));

printf("%d\n",sizeof(D));

return0;

}

A.1、4、84、82B.4、4、82、84

C.4、4、84、82D.1、4、82、82

解答:

A。

类B的大小为4是因为B中有指针vptr,可参考图9-1。

23:

下面代码的输出结果是什么?

intmain(){

typedefvoid(*Fun)(void);

Baseb;

FunpFun=NULL;

cout<<"虚函数表地址:

"<<(int*)(&b)<

cout<<"虚函数表_第―个函数地址:

"<<(int*)*(int*)(&b)<

//Invokethefirstvirtualfunction

pFun=(Fun)*((int*)*(int*)(&b));//Base:

:

f()

pFun();

pFun=(Fun)*((int*)*(int*)(&b)+1);//Base:

:

g()

pfun();

pFun==(Fun)*((int*)*(int*)(&b)+2);//Base:

:

h()

pFun();

return0;

}

解答:

实际运行结果如下:

(Windows7+VS2010/Linux2.6.22+GCC4.1.3)

虚函数表地址:

001EFC90

虚函数表一第一个函数地址:

00A47874

Base:

:

fBase:

gBase:

h

通过这个示例,我们可以看到,我们可以通过强行把&b转成int*,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base:

:

f(),这在上面的程序中得到了验证(把int*强制转成了函数指针)。

图示如下:

&b

注意:

在上面这个图中,在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符’\0’—样,其标志了虚函数表的结束。

这个结束标志的值在不同的编译器下是不同的。

在Windows7+VS2010下,这个值是NULL。

同时类Base的对象大小为4,即类中仅有一个指针vplr(指向虚函数表)。

24:

画出下列类A、B、C、D的对象的虚函数表。

classA{

public:

virtualvoida(){cout<<"a()inA"<

};

classB:

publicA{

public:

voida(){cout<<"a()inB"<

voidb(){cout<<"b()inB"<

};

classC:

publicA{

public:

voida(){cout<<"a()inC"<

};

classD:

publicB,publicC{

public:

voida(){cout<<"a()inD"<

voidd(){cout<<"d()inD"<

};

解答:

如下所示:

A:

:

a

A:

:

b

A:

:

c

A:

:

d

A的对象

vptr

以上为A类对象的虚函数表,每个格子记录一个函数的地址。

B:

:

a

B:

:

b

A:

:

c

A:

:

d

B的对象vptr

 

可见,单基继承时,仅有一个vptr。

B类中的函数a与b覆盖了A类中的同名函数,故虚函数表中对应位置替换为新函数的地址。

c的对象vptr

D的对象

可见,单基继承时,仅有一个vptr。

C类中的函数a与b覆盖了A类中的同名函数,故虚函数表中对应位置替换为新函数的地址。

可见,多基继承时,有几个基类就有几个vptr。

D类中的函数a与d覆盖了B类中的同名函数,故虚函数表中对应位置替换为新函数的地址。

D类中的函数a与d覆盖了C类中的同名函数,故虚函数表中对应位置替换为新函数的地址。

25:

如下代码的输出结果是什么?

classX{};

classY:

publicvirtualX{};classZ:

publicvirtualX{};classA:

publicY,publicZ{};intmain(){

cout<<"sizeof(X):

"<

"<

cout<<"sizeof(Z):

"<

"<

解答:

1,4,4,8。

X类是空的,为什么sizeof(X)=l呢?

事实上,在前面章节介绍struct的sizeof值时已经介绍过原因,这是因为事实上X并不是空的,它有一个隐晦的1字节,那是编译器安插进去的一个byte。

这使得classX的objects得以在内存中配置独一无二的地址。

下图给出X、Y、Z的对象布局。

 

derivedclassY

 

事实上Y和Z的大小受到三个因素的影响:

1)语言本身所造成的额外负担。

当语言支持虚基类(virtualbaseclasses)时,就会造成一些额外负担。

在子类中,这个额外负担反映在bptr上,即增加了一个指针。

2)编译器对于特殊情况所做的优化处理。

现在的编译器一般会对空虚基类提供特殊支持(如VS2010)。

在这个策略下,一个空虚基类由于有了一个指针bptr,故不需再像空类一样占用1个字节,也就是说因为有了成员,就不再需要原本为了空类而安插的1个byte。

3)Alignment的限制(如果需要的话),就是字节对齐。

因此,Y和Z的大小都是4字节,其对象内仅包含一个bptr,且不需要对齐处理。

下面我们讨论A的大小。

这里需要注意的是:

如图9-2所示,classA的占用空间由下面几部分构成:

1)被大家共享的唯——个classX实体,大小为1B,目前的编译器通常都做了优化,省去这单单为了占位的1B,故此部分为0;

2)BaseclassY的大小(为4B)减去“因virtualbaseclassX而配置”的大小(本题中为0),故结果为4B;

3)BaseclassZ的大小(为4B)减去“因virtualbaseclassX而配置”的大小(本题中为0),故结果为4B;

4)classA自己的大小:

0B;

前述四项总和,共8B。

然后考虑字节对齐,不需要对齐,故sizeof(A)为8。

注意:

关于C++对象模型的更深入研究,可参考《深度探索(:

抖对象模型》一书。

26:

假设A为抽象类,下列声明()是正确的?

(2012•迅雷)

A.Afun(int);B.A*p;C.intfun(A)D.AObj;

解答:

B。

若在A和C选项中的A后面加上&或*就是对的。

27:

抽象类为什么不能实例化?

(2011•阿里云)

解答:

28:

C++用于的4个操作符是_,_,_,_?

(2012•腾讯)

解答:

,,

在引入命名的强制类型转换操作符之前,显式强制转换用圆括号将类型括起来实现:

int*ip;

char*pc=(char*)ip;

效果与使用reinterpret_cast符号相同。

int*ip;

char*pc==reinterpret_cast(ip);

const_cast,顾名思义,将转换掉表达式的const性质。

constchar*pc_str;

char*pc=const_cast(pc_str);

只有使用const_cast才能将const性质转换掉。

在这种情况下,试图使用其他三种形式的强制转换都会导致编译时的错误。

类似地,除了添加或删除const特性,用const_cast符来执行其他任何类型转换,都会引起编译错误。

29:

怎么才能让ptr指向value?

constdoublevalue=0.0f;

double*ptr=NULL

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1