}
7.类成员函数的重载、覆盖和隐藏区别?
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual关键字。
c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。
此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。
此时,基类的函数被隐藏(注意别与覆盖混淆)
//从函数名、参数、有无virtual关键字、范围几方面比较。
8.面向对象的三个基本特征,并简单叙述之?
1.封装:
将客观事物抽象成类,每个类对自身的数据和方法实行protection(private,protected,public)
2.继承:
广义的继承有三种实现形式:
实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。
前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
3.多态:
是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简单的说,就是一句话:
允许将子类类型的指针赋值给父类类型的指针。
9.请简单描述Windows内存管理的方法。
内存管理是操作系统中的重要部分,两三句话恐怕谁也说不清楚吧~~
我先说个大概,希望能够抛砖引玉吧
当程序运行时需要从内存中读出这段程序的代码。
代码的位置必须在物理内存中才能被运行,由于现在的操作系统中有非常多的程序运行着,内存中不能够完全放下,所以引出了虚拟内存的概念。
把哪些不常用的程序片断就放入虚拟内存,当需要用到它的时候在load入主存(物理内存)中。
这个就是内存管理所要做的事。
内存管理还有另外一件事需要做:
计算程序片段在主存中的物理位置,以便CPU调度。
内存管理有块式管理,页式管理,段式和段页式管理。
现在常用段页式管理
块式管理:
把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。
这样会造成很大的浪费,平均浪费了50%的内存空间,但是易于管理。
页式管理:
把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。
段式管理:
把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。
一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上(计算机最耗时间的大家都知道是I/O吧)。
段页式管理:
结合了段式管理和页式管理的优点。
把主存分为若干页,每一页又分为若干段。
好处就很明显,不用我多说了吧。
各种内存管理都有它自己的方法来计算出程序片断在主存中的物理地址,其实都很相似。
这只是一个大概而已,不足以说明内存管理的皮毛。
无论哪一本操作系统书上都有详细的讲解。
10.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;
}
Csdn文档摘要:
The_onexitfunctionispassedtheaddressofafunction(func)tobecalledwhentheprogramterminatesnormally.Successivecallsto_onexitcreatearegisteroffunctionsthatareexecutedinLIFO(last-in-first-out)order.Thefunctionspassedto_onexitcannottakeparameters.
11.const符号常量;
(1)constchar*p
(2)charconst*p
(3)char*constp
说明上面三种描述的区别.
如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
(1)constchar*p
一个指向char类型的const对象指针,p不是常量,我们可以修改p的值,使其指向不同的char,但是不能改变它指向非char对象,如:
constchar*p;
charc1='a';
charc2='b';
p=&c1;//ok
p=&c2;//ok
*p=c1;//error
(3)char*constp
此时*p可以修改,而p不能修改。
(4)constchar*constp
这种是地址及指向对象都不能修改。
12.下面是C语言中两种if语句判断方式。
请问哪种写法更好?
为什么?
intn;
if(n==10)//第一种判断方式
if(10==n)//第二种判断方式
如果少了个=号,编译时就会报错,减少了出错的可能行,可以检测出是否少了=。
13.*p++自增p还是p所指向的变量?
后缀++和--操作符本质上比前缀一目操作的优先级高,因此*p++和*(p++)等价,它自增p并返回p自增之前所指向的值。
要自增p指向的值,使用(*p)++,如果副作用的顺序无关紧要也可以使用++*p。
14.#pragma
#pragam指令提供了一种单一的明确定义的“救生舱”,可以用作各种(不可移植的)实现相关的控制和扩展:
源码表控制、结构压缩、警告去除(就像lint的老/*NOTREACHED*/注释),等等。
(区分C#的param用于可变参数)
15.“#pragmaonce”
这是某些预处理器实现的扩展,用于使头文件自我识别;它跟#ifndef技巧等价,不过移植性差些。
16.进程间通信的方式有?
进程间通信的方式有共享内存(sharedmemory),管道(pipe),套接字(Socket),消息队列(messagequeue),DDE等
17.如何打印出当前源文件的文件名以及源文件的当前行号?
cout<<__FILE__;
cout<<__LINE__;
__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。
18.如何判断一段程序是由C编译程序还是由C++编译程序编译的?
答:
#ifdef__cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
19.Newdelete与mallocfree的联系与区别?
都是在堆(heap)上进行动态的内存操作。
用malloc函数需要指定内存分配的字节数并且不能初始化对象,new会自动调用对象的构造函数。
delete会调用对象的destructor,而free不会调用对象的destructor。
20.介绍一下STL,详细说明STL如何实现vector。
STL(标准模版库,StandardTemplateLibrary)它由容器、算法、迭代器组成。
STL有以下的一些优点:
可以方便容易地实现搜索数据或对数据排序等一系列的算法;
调试程序时更加安全和方便;
即使是人们用STL在UNIX平台下写的代码你也可以很容易地理解(因为STL是跨平台的)。
vector实质上就是一个动态数组,会根据数据的增加,动态的增加数组空间。
21.指针和引用有什么分别;
如果传引用比传指针安全,为什么?
如果我使用常量指针难道不行吗?
(1)引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值.
(2)不存在NULL引用,引用必须与合法的存储单元关联;而指针则可以是NULL.
(3)引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象.给引用赋值并不是改变它和原始对象的绑定关系.
(4)引用的创建和销毁并不会调用类的拷贝构造函数
(5)语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换.
(总结:
创建初始化;能否为Null;能否改变;创建和销毁;用法和实现;)
不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,所以显得很安全。
const指针仍然存在空指针,并且有可能产生野指针.
总的来说:
引用既具有指针的效率,又具有变量使用的方便性和直观性.
22.构造函数可否是虚函数,为什么?
析构函数呢,可否是纯虚的呢?
构造函数不能为虚函数,要构造一个对象,必须清楚地知道要构造什么,否则无法构造一个对象。
析构函数可以为纯虚函数。
简答题二
1.求下面函数的返回值(微软)
intfunc(x)
{
intcountx=0;
while(x)
{
countx++;
x=x&(x-1);
}
returncountx;
}
假定x=9999。
答案:
8
思路:
将x转化为2进制,看含有的1的个数。
2.什么是“引用”?
申明和使用“引用”要注意哪些问题?
答:
引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。
申明一个引用的时候,切记要对其进行初始化。
引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。
声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
不能建立数组的引用。
3.将“引用”作为函数参数有哪些特点?
(1)传递引用给函数与传递指针的效果是一样的。
这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。
因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。
而引用更容易使用,更清晰。
4.在什么时候需要使用“常引用”?
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
常引用声明方式:
const类型标识符&引用名=目标变量名;
例1
inta;
constint&ra=a;
ra=1;//错误
a=1;//正确
例2
stringfoo();
voidbar(string&s);
那么下面的表达式将是非法的:
bar(foo());
bar("helloworld");
原因在于foo()和"helloworld"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。
因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
引用型参数应该在能被定义为const的情况下,尽量定义为const。
5.将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?
格式:
类型标识符&函数名(形参列表及类型说明){//函数体}
好处:
在内存中不产生被返回值的副本;(注意:
正是因为这点原因,所以返回一个局部变量的引用是不可取的。
因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtimeerror!
注意事项:
(1)不能返回局部变量的引用。
这条可以参照EffectiveC++[1]的Item31。
主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。
这条可以参照EffectiveC++[1]的Item31。
虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。
例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memoryleak。
(3)可以返回类成员的引用,但最好是const。
这条原则可以参照EffectiveC++[1]的Item30。
主要原因是当对象的属性是与某种业务规则(businessrule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。
如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
(4)流操作符重载返回值申明为“引用”的作用:
流操作符<<和>>,这两个操作符常常希望被连续使用,例如:
cout<<"hello"<可选的其它方案包括:
返回一个流对象和返回一个流对象指针。
但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!
这无法让人接受。
对于返回一个流指针则不能连续使用<<操作符。
因此,返回一个流对象引用是惟一选择。
这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。
赋值操作符=。
这个操作符象流操作符一样,是可以连续使用的,例如:
x=j=10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。
因此引用成了这个操作符的惟一返回值选择。
例3
#include
int&put(intn);
intvals[10];
interror=-1;
voidmain()
{
put(0)=10;//以put(0)函数值作为左值,等价于vals[0]=10;
put(9)=20;//以put(9)函数值作为左值,等价于vals[9]=20;
cout<cout<}
int&put(intn)
{
if(n>=0&&n<=9)returnvals[n];
else{cout<<"subscripterror";returnerror;}
}
(5)在另外的一些操作符中,却千万不能返回引用:
+-*/四则运算符。
它们不能返回引用,EffectiveC++[1]的Item23详细的讨论了这个问题。
主要原因是这四个操作符没有sideeffect,因此,它们必须构造一个对象作为返回值,可选的方案包括:
返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。
根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。
静态对象的引用又因为((a+b)==(c+d))会永远为true而导致错误。
所以可选的只剩下返回一个对象了。
6.“引用”与多态的关系?
引用是除指针外另一个可以产生多态效果的手段。
这意味着,一个基类的引用可以指向它的派生类实例。
例4
ClassA;ClassB:
ClassA{...}; Bb;A&ref=b;
7.“引用”与指针的区别是什么?
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。
程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
此外,就是上面提到的对函数传ref和pointer的区别。
8.什么时候需要“引用”?
流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。
以上2-8参考:
9.结构与联合有和区别?
1.结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员(所有成员共用一块地址空间),而结构的所有成员都存在(不同成员的存放地址不同)。
2.对于联合的不同成员赋值,将会对其它成员重写, 原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的。
10.下面关于“联合”的题目的输出?
a)
#include
union
{
inti;
charx[2];
}a;
void main()
{
a.x[0]=10;
a.x[1]=1;
printf("%d",a.i);
}
答案:
266(低位低地址,高位高地址,内存占用情况是Ox010A)
b)
main()
{
Union
{ /*定义一个联合*/
inti;
struct
{ /*在联合中定义一个结构*/
charfirst;
charsecond;
}half;
}number;
number.i=0x4241; /*联合成员赋值*/
printf("%c%c\n",number.half.first,mumber.half.second);
number.half.first='a'; /*联合中结构成员赋值*/
number.half.second='b';
prin