C++题目.docx
《C++题目.docx》由会员分享,可在线阅读,更多相关《C++题目.docx(29页珍藏版)》请在冰点文库上搜索。
C++题目
C++程序员面试宝典
(1)
1.new、delete、malloc、free关系
(1)delete会调用对象的析构函数,new调用构造函数。
Malloc申请内存空间,free只会释放内存。
(2)malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。
(3)它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。
(4)对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。
注意new/delete不是库函数。
(5)都是在堆(heap)上进行动态的内存操作。
用malloc函数需要指定内存分配的字节数并且不能初始化对象,new会自动调用对象的构造函数。
delete会调用对象的destructor,而free不会调用对象的destructor.
2.delete与delete[]区别
delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。
delete与New配套,delete[]与new[]配套
MemTest*mTest1=newMemTest[10];
MemTest*mTest2=newMemTest;
int*pInt1=newint[10];
int*pInt2=newint;
delete[]pInt1;//-1-
delete[]pInt2;//-2-报错,要使用delete
delete[]mTest1;//-3-
delete[]mTest2;//-4-报错,使用delete
这就说明:
对于内建简单数据类型,delete和delete[]功能是相同的。
对于自定义的复杂数据类型,delete和delete[]不能互用。
delete[]删除一个数组,delete删除一个指针。
简单来说,用new分配的内存,用delete删除;用new[]分配的内存,用delete[]删除。
delete[]会调用数组元素的析构函数。
内部数据类型没有析构函数,所以问题不大。
如果你在用delete时没用括号,delete就会认为指向的是单个对象,否则,它就会认为指向的是一个数组。
3、继承优缺点
优点:
类继承是在编译时刻静态定义的,且可直接使用,类继承可以较方便地改变父类的实现。
缺点:
(1)因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。
(2)父类通常至少定义了子类的部分行为,父类的任何改变都可能影响子类的行为。
如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。
这种依赖关系限制了灵活性并最终限制了复用性。
4、什么是“引用”?
申明和使用“引用”要注意哪些问题?
答:
引用就是某个目标变量的“别名”(alias),对引用的操作与对变量直接操作效果完全相同。
引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。
声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元,不能建立数组的引用。
5、将“引用”作为函数参数有哪些特点?
(1)传递引用与传递指针的效果是一样的。
函数的形参成为主调函数中的实参的一个别名来使用,函数中对形参操作就是对主函数中的实参操作。
(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。
因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。
而引用更容易使用,更清晰。
6、在什么时候需要使用“常引用”?
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
常引用声明方式:
const类型标识符&引用名=目标变量名;
而在C++中,这些临时对象都是const类型的。
因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
引用型参数应该在能被定义为const的情况下,尽量定义为const。
7、“引用”与指针的区别是什么?
指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。
程序中使用指针,程序的可读性差;
而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
8、结构与联合有和区别?
(1)结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员(所有成员共用一块地址空间),而结构的所有成员都存在(不同成员的存放地址不同)。
(2)对于联合的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的。
9、关联、聚合(Aggregation)以及组合(Composition)的区别?
涉及到UML中的一些概念:
关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,从实现的角度讲,聚合可以表示为:
classA{...}classB{A*a;.....}
而组合表示contains-a的关系,关联性强于聚合:
组合类与被组合类有相同的生命周期,组合类要对被组合类负责,实现的形式是:
classA{...}classB{Aa;...}
10、面向对象的三个基本特征,并简单叙述之?
1.封装:
将客观事物抽象成类,每个类对自身的数据和方法通过修饰符实行保护
2.继承:
广义的继承有三种实现形式:
实现继承(指使用基类的属性和方法而无需额外编码的能力)
可视继承(子窗体使用父窗体的外观和实现代码)
接口继承(仅使用属性和方法,实现滞后到子类实现)。
前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。
3.多态:
是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简单的说,就是一句话:
允许将子类类型的指针赋值给父类类型的指针。
11、子类析构时要调用父类的析构函数吗?
析构函数调用的次序:
先派生类析构、后基类析构;在基类的的析构调用的时候,派生类的信息已经全部销毁了。
构造函数调用次序:
先调用基类的构造函数、后调用派生类的构造函数;
12、重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?
从定义上来说:
重载:
是指允许存在多个同名函数,而这些函数的参数表不同
重写:
是指子类重新定义父类虚函数的方法。
从实现原理上来说:
重载:
编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。
如:
有两个同名函数:
functionfunc(integer)和functionfunc(string)。
那么编译器做过修饰后的函数名称可能是这样的:
int_func、str_func。
对于这两个函数的调用,在编译器间就已经确定了,是静态的。
也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
重写:
和多态真正相关。
当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。
因此,这样的函数地址是在运行期绑定的(晚绑定)。
13、多态的作用?
主要是两个:
1.隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;
2.接口重用:
为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。
14、C++是不是类型安全的?
不是。
两个不同类型的指针之间可以强制转换(用reinterpretcast)。
C#是类型安全的。
15、main函数执行以前,还会执行什么代码?
全局对象的构造函数会在main函数之前执行。
16、描述内存分配方式以及它们的区别?
(1)从静态存储区域分配。
在程序编译的时候分配好了,这块内存在程序运行期间都存在。
例如全局变量,static变量。
(2)在栈上创建。
在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集。
(3)从堆上分配,亦称动态内存分配。
程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。
动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
17、struct和class的区别
struct的成员默认是公有的,而类的成员默认是私有的。
struct和class在其他方面是功能相当的。
感觉上结构仅仅象一堆缺乏封装和功能的开放的内存位,而类就象活的并且可靠的社会成员,它有智能服务,有牢固的封装屏障和一个良好定义的接口。
既然大多数人都这么认为,那么只有在你的类有很少的方法并且有公有数据(这种事情在良好设计的系统中是存在的!
)时,你也许应该使用struct关键字,否则,你应该使用class关键字。
18、请说出const与#define相比,有何优点?
Const作用:
定义常量、修饰函数参数、修饰函数返回值三个作用。
被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
1)const常量有数据类型,而宏常量没有数据类型。
编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
19、求出两个数中的较大者
Therearetwointvariables:
aandb,don’tuse“if”,“?
:
”,“switch”orotherjudgementstatements,findoutthebiggestoneofthetwonumbers.
答案:
((a+b)+abs(a-b))/2
20、C语言中结构化程序设计的三种基本控制结构
顺序结构、选择结构、循环结构
21、面的结构是合法的吗,如果是它做些什么?
inta=5,b=7,c;
c=a+++b;
上面的例子是完全合乎语法的。
问题是编译器如何处理它?
根据最处理原则,编译器应当能处理尽可能所有合法的用法。
因此,上面的代码被处理成:
c=a+++b;
因此,这段代码持行后a=6,b=7,c=12。
发现这个问题的最大好处是:
代码编写风格,代码的可读性,代码的可修改性
22、SQL语言概述
SQL(结构化查询语言)是关系数据库语言的一种国际标准,它是一种非过程化的语言。
通过编写SQL,我们可以实现对关系数据库的全部操作。
●数据定义语言(DDL)——建立和管理数据库对象
●数据操纵语言(DML)——用来查询与更新数据
●数据控制语言(DCL)——控制数据的安全性
事务处理系统的典型特点是具备ACID特征。
ACID指的是Atomic(原子的)、Consistent(一致的)、Isolated(隔离的)以及Durable(持续的),它们代表着事务处理应该具备的四个特征:
原子性:
组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分
一致性:
在事务处理执行之前和之后,数据是一致的。
隔离性:
一个事务处理对另一个事务处理没有影响。
持续性:
当事务处理成功执行到结束的时候,其效果在数据库中被永久纪录下来。
22、关系模型的基本概念
关系数据库以关系模型为基础,它有以下三部分组成:
●数据结构——模型所操作的对象、类型的集合
●完整性规则——保证数据有效、正确的约束条件
●数据操作——对模型对象所允许执行的操作方式
关系(Relation)是一个由行和列组成的二维表格,表中的每一行是一条记录(Record),每一列是记录的一个字段(Field)。
表中的每一条记录必须是互斥的,字段的值必须具有原子性。
23、数据库与T-SQL语言
关系数据库是表的集合,它是由一个或多个关系模式定义。
SQL语言中的数据定义功能包括对数据库、基本表、视图、索引的定义。
24、有关重载函数
(1)返回值类型不同构不成重载
(2)参数参数顺序不同能构成重载
(3)c++函数同名不同返回值不算重载!
函数重载是忽略返回值类型的。
---------------------------------------------
成员函数被重载的特征有:
1)相同的范围(在同一个类中);
2)函数名字相同;
3)参数不同;
4)virtual关键字可有可无。
5)成员函数中有无const(函数后面)也可判断是否重载
25、交换两个数的宏定义
交换两个参数值的宏定义为:
.#defineSWAP(a,b)(a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);
C++程序员面试宝典
(2)
1、Static作用是什么
首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。
2、什么是预编译,何时需要预编译?
预编译又称为预处理,是做些代码文本的替换工作,处理#开头的指令。
比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
c编译系统在对程序进行通常的编译之前,先进行预处理。
c提供的预处理功能主要有以下三种:
1)宏定义 2)文件包含 3)条件编译
使用的情况:
(1)总是使用不经常改动的大型代码体。
(2)程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。
在这种情况下,可以将所有包含文件预编译为一个预编译头。
3、进程和线程的区别
什么是进程(Process):
普通的解释就是,进程是程序的一次执行
什么是线程(Thread),线程可以理解为进程中的执行的一段程序片段。
两者间的差别:
(1)进程间是独立的,这表现在内存空间,上下文环境;
(2)线程运行在进程空间内。
(3)进程是无法突破进程边界存取其他进程内的存储空间;
而线程由于处于进程空间内,所以同一进程所产生的线程共享同一内存空间。
(4)同一进程中的两段代码不能够同时执行,除非引入线程。
(5)线程是属于进程的,当进程退出时该进程所产生的线程都会被强制退出并清除。
(6)线程占用的资源要少于进程所占用的资源。
进程和线程都可以有优先级。
(7)在线程系统中进程也是一个线程。
可以将进程理解为一个程序的第一个线程。
线程是指进程内的一个执行单元,也是进程内的可调度实体.与进程的区别:
(1)地址空间:
进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
(2)进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3)线程是处理器调度的基本单位,但进程不是.
(4)二者均可并发执行.
4、运算符优先级问题
(1)能正确表示a和b同时为正或同时为负的逻辑表达式是(D)。
A、(a>=0||b>=0)&&(a<0||b<0)
B、(a>=0&&b>=0)&&(a<0&&b<0)
C、(a+b>0)&&(a+b<=0)
D、a*b>0
(2)以下关于运算符优先顺序的描述中正确的是(C)。
A、关系运算符<算术运算符<赋值运算符<逻辑与运算符
B、逻辑与运算符<关系运算符<算术运算符<赋值运算符
C、赋值运算符<逻辑与运算符<关系运算符<算术运算符
D、算术运算符<关系运算符<赋值运算符<逻辑与运算符
5、判断字符串是否为回文
boolIsSymmetry(constchar*p)
{
assert(p!
=NULL);
constchar*q=p;
intlen=0;
while(*q++!
='\0')
len++;
boolbSign=true;
q=p+len-1;
if(0for(inti=0;iif(*p++!
=*q--){bSign=false;break;};
if(bSign==true)
printf("Yes!
\n");
else
printf("No!
\n");
returnbSign;
}
6、下面的代码输出是什么,为什么?
voidfoo(void)
{
unsignedinta=6;
intb=-20;
(a+b>6)?
puts(">6"):
puts("<=6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。
不管如何,这无符号整型问题的答案是输出是“>6”。
原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。
这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
7、用变量a给出下面的定义
a)一个整型数
b)一个指向整型数的指针
c)一个指向指针的的指针,它指向的指针是指向一个整型数
d)一个有10个整型数的数组
e)一个有10个指针的数组,该指针是指向一个整型数的
f)一个指向有10个整型数数组的指针
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h)一个有10个指针的数组,该指针指向一个函数,函数有一个整型参数并返回一个整型数
答案是:
a)inta;//Aninteger
b)int*a;//Apointertoaninteger
c)int**a;//Apointertoapointertoaninteger
d)inta[10];//Anarrayof10integers
e)int*a[10];//Anarrayof10pointerstointegers
f)int(*a)[10];//Apointertoanarrayof10integers
g)int(*a)(int);//Apointertoafunctionathattakesanintegerargumentandreturnsaninteger
h)int(*a[10])(int);//Anarrayof10pointerstofunctionsthattakeanintegerargumentandreturnaninteger
人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。
当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。
但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。
因为在被面试的这段时间里,我确定我知道这个问题的答案。
应试者如果不知道
所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?
8、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。
我首选的方案是:
while
(1)
{
}
一些程序员更喜欢如下方案:
for(;;)
{
}
这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。
如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。
如果他们的基本答案是:
“我被教着这样做,但从没有想到过为什么。
”这会给我留下一个坏印象。
第三个方案是用goto
Loop:
...
gotoLoop;
应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。
9、预处理器标识#error的目的是什么?
如果你不知道答案,请看参考文献1。
这问题对区分一个正常的伙计和一个书呆子是很有用的。
只有书呆子才会读C语言课本的附录去找出象这种
问题的答案。
当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。
10、写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个
#defineMIN(A,B)((A)<=(B)(A):
(B))
这个测试是为下面的目的而设的:
1).标识#define在宏中应用的基本知识。
这是很重要的,因为直到嵌入(inline)操作符变为标准C的一部分,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
2).三重条件操作符的知识。
这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。
3).懂得在宏中小心地把参数用括号括起来
4).我也用这个问题开始讨论宏的副作用,例如:
当你写下面的代码时会发生什么事?
least=MIN(*p++,b);
11、关键字static的作用是什么?
这个简单的问题很少有人能回答完全。
在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。
它是一个本地的全局变量。
3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。
那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。
这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
12、对于一个频繁使用的短小函数,在C语言中应用什么