程序员面试试题深刻剖析资料.docx
《程序员面试试题深刻剖析资料.docx》由会员分享,可在线阅读,更多相关《程序员面试试题深刻剖析资料.docx(14页珍藏版)》请在冰点文库上搜索。
程序员面试试题深刻剖析资料
面试例题1:
如果鸟是可以飞的,那么鸵鸟是鸟么?
鸵鸟如何继承鸟类?
[美国某著名分析软件公司2005年面试题]
解析:
如果所有鸟都能飞,那鸵鸟就不是鸟!
回答这种问题时,不要相信自己的直觉!
将直觉和合适的继承联系起来还需要一段时间。
根据题干可以得知:
鸟是可以飞的。
也就是说,当鸟飞行时,它的高度是大于0的。
鸵鸟是鸟类(生物学上)的一种。
但它的飞行高度为0(鸵鸟不能飞)。
不要把可替代性和子集相混淆。
即使鸵鸟集是鸟集的一个子集(每个驼鸟集都在鸟集内),但并不意味着鸵鸟的行为能够代替鸟的行为。
可替代性与行为有关,与子集没有关系。
当评价一个潜在的继承关系时,重要的因素是可替代的行为,而不是子集。
答案:
如果一定要让鸵鸟来继承鸟类,可以采取组合的办法,把鸟类中的可以被鸵鸟继承的函数挑选出来,这样鸵鸟就不是“akindof”鸟了,而是“hassomekindof”鸟的属性而已。
代码如下:
#include
#include
usingnamespacestd;
classbird
{
public:
voideat();
voidsleep();
voidfly();
};
classostrich
{
public:
birdeat(){cout<<"ostricheat";};
birdsleep(){cout<<"ostrichsleep";};
};
intmain()
{
ostrichxiaoq;
xiaoq.eat();
xiaoq.sleep();
return0;
}
面试例题2:
Findthedefectsineachofthefollowingprograms,andexplainwhyitisincorrect.(找出下面程序的错误,并解释它为什么是错的。
)[中国台湾某著名杀毒软件公司2005年面试题]
#include
usingnamespacestd;
classBase{
public:
intval;
Base(){val=1;};
};
classDerive:
Base{
public:
intval;
Derive(inti){val=Base:
:
val+i;};
};
intmain(int,char**,char**){
Derived(10);
cout<<d.Base:
:
val<<endl<<d.val<<endl;
return0;
}
答案:
把classDerive:
Base改成classDerive:
publicBase。
解析:
这是个类继承问题。
如果不指定public,C++默认的是私有继承。
私有继承是无法继承并使用父类函数中的公有变量的。
扩展知识(组合)
若在逻辑上A是B的“一部分”(apartof),则不允许B从A派生,而是要用A和其他东西组合出B。
例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,而不是派生而成。
程序如下:
classEye
{
public:
voidLook(void);
};
classNose
{
public:
voidSmell(void);
};
classMouth
{
public:
voidEat(void);
};
classEar
{
public:
voidListen(void);
};
classHead
{
public:
voidLook(void){m_eye.Look();}
voidSmell(void){m_nose.Smell();}
voidEat(void){m_mouth.Eat();}
voidListen(void){m_ear.Listen();}
private:
Eyem_eye;
Nosem_nose;
Mouthm_mouth;
Earm_ear;
};
Head由Eye、Nose、Mouth、Ear组合而成。
如果允许Head从Eye、Nose、Mouth、Ear派生而成,那么Head将自动具有Look、Smell、Eat、Listen这些功能。
程序十分简短并且运行正确,但是下面这种设计方法却是不对的。
classHead:
publicEye,publicNose,publicMouth,publicEar
{
};
面试例题3:
Findthedefectsineachofthefollowingprograms,andexplainwhyitisincorrect.(找出下面程序的错误,并解释它为什么是错的。
)[德国某著名软件咨询企业2005年面试题]
classbase{
private:
inti;
public:
base(intx){i=x;}
};
classderived:
publicbase{
private:
inti;
public:
derived(intx,inty){i=x;}
voidprintTotal(){inttotal=i+base:
:
i;}
};
解析:
要在子类中设定初始成员变量,把derived(intx,inty)改成derived(intx,inty):
base(x)。
答案:
代码如下:
classbase
{
protected:
//这里的访问属性需要改变
inti;
public:
base(intx){i=x;}
};
classderived:
publicbase
{
private:
inti;
public:
derived(intx,inty):
base(x)//以前没有初始化基类的成员变量
{
i=y;
}
voidprintTotal()
{
inttotal=i+base:
:
i;
}
};
32.请说出const与#define相比,有何优点?
答案:
1)const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
33.简述数组与指针的区别?
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。
指针可以随时指向任意类型的内存块。
(1)修改内容上的差别
chara[]=“hello”;
a[0]=‘X’;
char*p=“world”;//注意p指向常量字符串
p[0]=‘X’;//编译器不能发现该错误,运行时错误
(2)用运算符sizeof可以计算出数组的容量(字节数)。
sizeof(p),p为指针得到的是一个指针变量的字节数,而不是p所指的内存容量。
C++/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。
注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
chara[]="helloworld";
char*p=a;
cout<cout<计算数组和指针的内存容量
voidFunc(chara[100])
{
cout<}
34.类成员函数的重载、覆盖和隐藏区别?
答案:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。
此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。
此时,基类的函数被隐藏(注意别与覆盖混淆)
35.Therearetwointvariables:
aandb,don’tuse“if”,“?
:
”,“switch”orotherjudgementstatements,findoutthebiggestoneofthetwonumbers.
答案:
((a+b)+abs(a-b))/2
36.如何打印出当前源文件的文件名以及源文件的当前行号?
答案:
cout<<__FILE__;
cout<<__LINE__;
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。
37.main主函数执行完毕后,是否可能会再执行一段代码,给出说明?
答案:
可以,可以用_onexit注册一个函数,它会在main之后执行intfn1(void),fn2(void),fn3(void),fn4(void);
voidmain(void)
{
Stringstr("zhanglin");
_onexit(fn1);
_onexit(fn2);
_onexit(fn3);
_onexit(fn4);
printf("Thisisexecutedfirst.\n");
}
intfn1()
{
printf("next.\n");
return0;
}
intfn2()
{
printf("executed");
return0;
}
intfn3()
{
printf("is");
return0;
}
intfn4()
{
printf("This");
return0;
}
The_onexitfunctionispassedtheaddressofafunction(func)tobecalledwhentheprogramterminatesnormally.Successivecallsto_onexitcreatearegisteroffunctionsthatareexecutedinLIFO(last-in-first-out)order.Thefunctionspassedto_onexitcannottakeparameters.
38.如何判断一段程序是由C编译程序还是由C++编译程序编译的?
答案:
#ifdef__cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
39.文件中有一组整数,要求排序后输出到另一个文件中
答案:
#include
#include
usingnamespacestd;
voidOrder(vector&data)//bubblesort
{
intcount=data.size();
inttag=false;//设置是否需要继续冒泡的标志位
for(inti=0;i{
for(intj=0;j{
if(data[j]>data[j+1])
{
tag=true;
inttemp=data[j];
data[j]=data[j+1];
data[j+1]=temp;
}
}
if(!
tag)
break;
}
}
voidmain(void)
{
vectordata;
ifstreamin("c:
\\data.txt");
if(!
in)
{
cout<<"fileerror!
";
exit
(1);
}
inttemp;
while(!
in.eof())
{
in>>temp;
data.push_back(temp);
}
in.close();//关闭输入文件流
Order(data);
ofstreamout("c:
\\result.txt");
if(!
out)
{
cout<<"fileerror!
";
exit
(1);
}
for(i=0;iout<40.链表题:
一个链表的结点结构
structNode
{
intdata;
Node*next;
};
typedefstructNodeNode;
(1)已知链表的头结点head,写一个函数把这个链表逆序(Intel)
Node*ReverseList(Node*head)//链表逆序
{
if(head==NULL||head->next==NULL)
returnhead;
Node*p1=head;
Node*p2=p1->next;
Node*p3=p2->next;
p1->next=NULL;
while(p3!
=NULL)
{
p2->next=p1;
p1=p2;
p2=p3;
p3=p3->next;
}
p2->next=p1;
head=p2;
returnhead;
}
(2)已知两个链表head1和head2各自有序,请把它们合并成一个链表依然有序。
(保留所有结点,即便大小相同)
Node*Merge(Node*head1,Node*head2)
{
if(head1==NULL)
returnhead2;
if(head2==NULL)
returnhead1;
Node*head=NULL;
Node*p1=NULL;
Node*p2=NULL;
if(head1->datadata)
{
head=head1;
p1=head1->next;
p2=head2;
}
else
{
head=head2;
p2=head2->next;
p1=head1;
}
Node*pcurrent=head;
while(p1!
=NULL&&p2!
=NULL)
{
if(p1->data<=p2->data)
{
pcurrent->next=p1;
pcurrent=p1;
p1=p1->next;
}
else
{
pcurrent->next=p2;
pcurrent=p2;
p2=p2->next;
}
}
if(p1!
=NULL)
pcurrent->next=p1;
if(p2!
=NULL)
pcurrent->next=p2;
returnhead;
}
(3)已知两个链表head1和head2各自有序,请把它们合并成一个链表依然有序,这次要求用递归方法进行。
(Autodesk)
答案:
Node*MergeRecursive(Node*head1,Node*head2)
{
if(head1==NULL)
returnhead2;
if(head2==NULL)
returnhead1;
Node*head=NULL;
if(head1->datadata)
{
head=head1;
head->next=MergeRecursive(head1->next,head2);
}
else
{
head=head2;
head->next=MergeRecursive(head1,head2->next);
}
returnhead;
}
[C/C++]C/C++笔试、面试题目大汇总[41-45]
bioeconomy发表于2006-3-2220:
28:
00
41.分析一下这段程序的输出(Autodesk)
classB
{
public:
B()
{
cout<<"defaultconstructor"<}
~B()
{
cout<<"destructed"<}
B(inti):
data(i)//B(int)worksasaconverter(int->instanceofB)
{
cout<<"constructedbyparameter"<private:
intdata;
};
BPlay(Bb)
{
returnb;
}
(1)results:
intmain(intargc,char*argv[])constructedbyparameter5
{destructedB(5)形参析构
Bt1=Play(5);Bt2=Play(t1); destructedt1形参析构
return0; destructedt2 注意顺序!
}destructedt1
(2)results:
intmain(intargc,char*argv[])constructedbyparameter5
{destructedB(5)形参析构
Bt1=Play(5);Bt2=Play(10); constructedbyparameter10
return0; destructedB(10)形参析构
}destructedt2 注意顺序!
destructedt1
42.写一个函数找出一个整数数组中,第二大的数(microsoft)
答案:
constintMINNUMBER=-32767;
intfind_sec_max(intdata[],intcount)
{
intmaxnumber=data[0];
intsec_max=MINNUMBER;
for(inti=1;i{
if(data[i]>maxnumber)
{
sec_max=maxnumber;
maxnumber=data[i];
}
else
{
if(data[i]>sec_max)
sec_max=data[i];
}
}
returnsec_max;
}
43.写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数。
KMP算法效率最好,时间复杂度是O(n+m),详见:
44.多重继承的内存分配问题:
比如有classA:
publicclassB,publicclassC{}
那么A的内存结构大致是怎么样的?
这个是compiler-dependent的,不同的实现其细节可能不同。
如果不考虑有虚函数、虚继承的话就相当简单;否则的话,相当复杂。
可以参考《深入探索C++对象模型》,或者:
45.如何判断一个单链表是有环的?
(注意不能用标志位,最多只能用两个额外指针)
structnode{charval;node*next;}
boolcheck(constnode*head){}//returnfalse:
无环;true:
有环
一种O(n)的办法就是(搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重合,反之亦然):
boolcheck(constnode*head)
{
if(head==NULL)returnfalse;
node*low=head,*fast=head->next;
while(fast!
=NULL&&fast->next!
=NULL)
{
low=low->next;
fast=fast->next->next;
if(low==fast)returntrue;
}
returnfalse;
}