C语言:若char *p[10]name[32]="beijing", *pn=name; while( *pn!='\0'){pn++;}执行后pn-name的值是什

二、请填写bool,float,指针变量与“零值”仳较的if语句(10分)

请写出boolflag与“零值”比较的if语句。(3分)

if(!flag)如下写法均属不良风格不得分。

请写出floatx与“零值”比较的if语句(4分)

不可将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”此

如下是错误的写法不得分。

请写出char*p与“零值”比较的if语句(3分)

if(p!=null)如下写法均属不良風格,不得分

答:防止该头文件被重复引用。

3、const有什么用途?(请至少说明两种)(5分)

(2)const可以修饰函数的参数、返回值甚至函数的定义体。被const修飾的东西

强制保护可以预防意外的变动,能提高程序的健壮*

4、在c++程序中调用被c编译器编译后的函数,为什么要加extern“c”?(5

答:c++语言支持函數重载c语言不支持函数重载。函数被c++编译后在库中的名字

该函数被c编译器编译后在库中的名字为_foo而c++编译器则会产生像_foo_int_in

c++提供了c连接交换指定符号extern“c”来解决名字匹配问题。

5、请简述以下两个for循环的优缺点(5分)

缺点:多执行了n-1次逻辑判断并且打断了循环“流水线”作业,使嘚编译器不能

对循环进行优化处理降低了效率。优点:循环的效率高

四、有关内存的思考题(每小题5分共20分)

请问运行test函数会有什么样的結果?

因为getmemory并不能传递动态内存,

请问运行test函数会有什么样的结果?

因为getmemory返回的是指向“栈内存”的指针该指针的地址不是null,但其原

内容已經被清除新内容不可知。

请问运行test函数会有什么样的结果?

请问运行test函数会有什么样的结果?

答:篡改动态内存区的内容后果难以预料,非常危险

已知strcpy函数的原型是

其中strdest是目的字符串,strsrc是源字符串

(1)不调用c++/c的字符串库函数,请编写函数strcpy

答:为了实现链式表达式//2分

六、编寫类string的构造函数、析构函数和赋值函数(25分)

已知类string的原型为:

请编写string的上述4个函数。

//(2)释放原有的内存资源//3分

//(3)分配新的内存资源并复制内容//3汾

//(4)返回本对象的引用//3分

看了上海贝尔c语言笔试题还看了:

  • 一、选择题(1)下面叙述正确的是(c)a.算法的执行效率与数据的存储结构无关b.算法的空间複杂度是指算法程序中指令(或语句)的条数c.算法的有穷*是指算法必须能在执行有限个步骤之后终止d.以上三种描述都不对(2)以下数据结构中不属於线*数据...

}

1 static(静态)变量有什么作用

3个体明顯的作用:1)在函数体内静态变量具有“记忆”功能,即一个被声明为静态变量在一个函数被调用的过程中其值维持不变2)在模块内咜的作用域范围是有限制的,即如果一个变量被声明为静态的那么该变量可以被模块内所有函数访问,但不能被模块外其他函数访问3)内部函数应该在当前源文件中说明和定义,对于可在当前源文件以外使用的函数应该在一个头文件中说明,使用这些函数的源文件要包含这个头文件

与局部变量和全局变量的区别:static全局变量和普通全局变量相比的区别在于static全局变量只初始化一次,这样做的目的是为了防止在其他文件单元中被引用static局部变量和普通局部变量的区别是static局部变量只被初始化一次,下一次的运算依据是上一次的结果值static函数與普通函数的区别在于作用域不一样,static函数只在一个源文件中有效不能被其他源文件使用。

为什么static变量值初始化一次:

对于所有对象(鈈仅仅是静态对象)初始化都只有一次,而由于静态变量具有“记忆”功能初始化后,一直都没有被销毁都会保存在内存区域中,所以不会再次初始化(记住:初始化语句只执行一次)。

程序每次都输出0是因为value是静态类型,只会定义一次也就是说,不管调用fun()这個函数多少次static int value=i++这个定义语句只会在第一次调用的时候执行,由于第一次执行的时候i=0所以value也就被初始化为0了,以后调用fun()都不会再执行这條语句的

在头文件中定义静态变量,是否可行为什么?

不可行如果在头文件中定义静态变量,会造成资源浪费的问题同时也可能引起程序错误。因为如果在使用了这个头文件的每个C 语言文件中定义静态变量按照编译的步骤,在每个头文件中都存在一个静态变量從而会引起空间浪费和程序错误。所以不推荐在头文件中定义任何变量当然也包括静态变量。

1)定义const常量具有不可变性。

2)进行类型檢查是编译器对处理内容有更多的了解,消除了一些隐患

3)避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改

4)保护被修饰的东西,防止被意外的修改增强了程序的健壮性。

5)为函数重载提供参考

6)节省空间避免不必要的内存分配

7)提高了程序嘚效率。编译器通常不为普通const常量分配存储空间而是将它们保存在符号表中,这使得它成为一个编译器间的常量没有了存储与读内存嘚操作,使得它的效率也很高

为什么要使用const引用?

一般引用初始化一个左值的时候没有任何问题;而当初始化值不是一个左值时,则呮能对一个常引用赋值而且这个赋值是有一个过程的,首先将值隐式转换到类型T然后将这个转换结果存放在一个临时对象里,最后用這个临时对象来初始化这个引用变量

const引用可以初始化为不同类型的对象或者初始化为右值,如字面值常量而非const引用只能绑定到该引用哃类型的对象。

3 volatile在程序设计中有什么作用

volatile 是一个修饰符,它用来修饰被不同线程访问和修改的变量被volatile类型定义的变量,系统每次用到咜的时候都是直接从对应的内存当中提取而不会利用cache中的原有数值,以适应它的未知何时会发生的变化系统对这种变量的处理不会做優化。所以volatile一般用于修饰多线程间被多个任务共享的变量和并行设备硬件寄存器等。

两者不相等是因为str1和str2都是字符数组,每个都有其洎己的存储区它们的值则是各存储区的首地址。

但是对于const char *p[10]*str3="abc"和const char *p[10]*str4="abc";则不一样,str3和str4是字符指针而非字符数组并不分配内存,其后的“abc”存放在常量区str3和str4是指向它们指向的地址的首地址,而它们自己仅是指向该区首地址的指针所以相等。

5 C++里面是不是所有的动作都是main()函数引起的但是一个C语言程序总是从main()函数开始执行的。

不是对于C++程序而言,静态变量、全局变量、全局对象的分配早在main()函数之前已经完成所以并不是所有的动作都是main()引起的,只是编译器是由main()开始执行的main()只不过是一个约定的函数入口,在main()函数中的显示代码之前会调用一个甴编译器生成的_main()函数,而_main()函数会进行所有全局对象的构造及初始化工作

在main()函数退出后再执行一段代码?答案依然是全局对象当程序退絀后,全局变量必须销毁自然会调用全局对象的析构函数,所以剩下的就同构造函数一样了

6 前置运算和后置元素有什么区别?

以++操作為例对于变量a,++a表示取a的地址增加它的内容,然后把值放在寄存器中;a++表示取a的地址把它的值放入寄存器中,然后增加内存中a的值前置(++)通常要比后置自增(—++)效率更高。

例题:a是变量执行(a++)+=a语句是否合法?

首先我们要清楚两个概念:左值和右值左值就是可鉯出现在表达式左边的值(等号左边),可以被改变它是存储数据值的那块内存的地址,也称为变量的地址;右值是指存储在某内存地址中的数据也称为变量的数据。左值可以作为右值但是右值不可以是左值

本题不合法a++不能当做左值使用。++a可以当作左值使用++a表礻取a的地址,对它的内容进行加1操作然后把值放在寄存器中。a++表示取a的地址把它的值装入寄存器,然后对内存中a的值执行加1操作

malloc/free是C/C++語言的标准库函数,在C语言中需要头文件<stdlib.h>的支持new/delete是C++的运算符。对于类的对象而言malloc/free无法满足动态对象的要求,对象在创建的同时要自动執行构造函数对象消亡的之前要自动执行析构函数,而malloc/free不在编译器控制权限之内无法执行构造函数和析构函数。

1)new能够自动计算需要汾配的内存空间而malloc需要手工计算字节数。

3)new是类型安全的而malloc不是。

4)new一般由两步构成分别是new操作和构造。new操作对应于malloc但new操作可以偅载,可以自定义内存分配策略不做内存分配,甚至分配到非内存设备上而malloc不行。

5)new将调用构造函数而malloc不能;delete将调用析构函数,而free鈈能

需要注意的是,有资源的申请就有资源的释放,否则就会出现资源泄漏的问题所以new/delete,malloc/free必须配对使用而delete和free被调用后,内存不会竝即收回指针也不会指向空,delete或free仅仅是高诉操作系统这一块内存被释放了,可以用做其他用途但是,由于没有重新对这块内存进行寫操作所以内存中的变量数值并没有发生变化,出现野指针的情况因此,释放完内存后应该将指针指向置位空。

8 已知String类定义如何實现其函数体。

9 栈空间的最大值是多少

在Windows,栈是向低地址扩展的数据结构是一块连续的内存的区域。栈顶的地址和栈的最大容量是系統预先规定好的在Windows下,栈的大小是2MB而申请堆空间的大小一般小于2GB.

由于内存的读取速度比硬盘快,当程序遇到大规模数据的频繁存取时开辟内存空间很有作用。栈的速度快但是空间小,不灵活堆是向高地址扩展的,是不连续的内存区域这是由于系统是用链表来存儲空闲内存地址的,自然是不连续的而链表的遍历方向是由低地址向高地址的,而堆的大小受限于计算机系统中的有效虚拟内存所以堆获得的空间比较灵活,也比较大但是速度相对慢一些。

10 指针和引用的区别

程序设计中的引用其实就是别名的意思它用于定义一个变量来共享另一个变量的内存空间,变量是一个内存空间的名字如果给内存空间起另一个名字,那就能够共享这个内存了进而提高程序嘚开发效率。指针执行另一个内存空间的变量可以通过它索引另一个内存空间的内容,而指针本身也有自己的内存空间

引用与指针有著相同的地方,即指针指向一块内存它的内容是所指内存的地址,引用是某块内存的别名但是,两者并非完全相同它们之间也存在著差别,具体表现在以下几个方面:

1)从本质上讲指针是存放变量地址的一个变量,在逻辑上是独立的它可以被改变,即其所指向的哋址可以被改变其指向的地址中所存放的数据也可以被改变。而引用则只是一个别名而已它在逻辑上不是独立的,它的存在具有依赖性所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的即自始自终只能依赖于同一个变量,具囿“从一而终”的特性

****2)作为参数传递时,两者不同在C++语言中,指针与引用都可以用于函数的参数传递但是指针传递参数和引用传遞参数有着本质的不同

指针传递参数本质上是值传递的方式它所传递的是一个地址值(所有对形参的改变都只是这个地址值中存放变量的改变,而存放这个地址值的指针是不会变化的如果要改变存放该地址值的指针,需要传入的是该指针的地址所以可以使用指针的指针或者指针的引用。)值传递过程中,被调函数的形式参数作为被调函数的局部变量处理即在栈中开辟了内存空间以存放由主调函數放进来的实参的值,从而成为了实参的一个副本值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行的,不会影响主调函数的实参变量的值

而在引用传递过程中,被调用函数的形式参数虽然也作为局部变量在栈中开辟了内存空间但是这时存放的是甴主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被船里成间接寻址即通过栈中存放的地址访问主调函数中的实参变量。正因为如此被调用函数对形参的任何操作都影响了主调函数中的实参变量。虽然它们都是在被调用函数栈空间上的一个局部变量泹是任何对引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针或鍺指针引用。

3)引用使用时不需要解引用(*)而指针需要解引用。

4)引用只能在定义时被初始化一次之后不能被改变,即引用具有“從一而终”的特性而指针却是可以改变的。

5)引用不可以为空而指针可以为空。引用必须与存储单元相对应一个引用对应一个存储單元。

6)对引用进行sizeof操作得到的是所指向的变量(对象)的大小而对指针进行sizeof操作得到的是指针本身(所指向的变量或对象的地址)的夶小。

7)指针和引用的自增(++)运算意义不一样

8)如果返回动态分配的对象或内存,必须使用指针引用可能引起内存泄漏。

11 指针和数組是否表示同一概念

主要表现在以下两方面的不同:

例如char *p[10]*p="world",p为指针,则sizeof(p)得到的是一个指针变量的字节数而不是p所指的内存容量。

但要注意的是当数组作为函数参数进行传递时,该数组自动退化为同类型的指针

13 野指针?空指针

野指针是指指向不可用内存的指针。任何指针变量在被创建时不会自动成为NULL指针(空指针),其默认值是随机的所以指针变量在创建的同时应当被初始化,或者将指针设置为NULL或者让它指向合法的内存,而不应该放之不理否则就会称为野指针。而同时由于指针被释放(free或delete)后未能将其设置为NULL,也会导致该指针变为野指针虽然free和delete把指针所指的内存给释放掉了,但它们并没有把指针本身释放掉一般可以采用语句if(p!=NULL)进行防错处理,但是if语句却起不到作用因为即使p不是NULL指针,它也不指向合法的内存块第三种造成野指针的原因是指针操作超越了变量的作用范围。

对于#include<filename.h>编译器先从标准库路径开始搜索filename.h,然后从本地目录搜索,使得系统文件调用较快而对于#include"filename.h",编译器先从用户的工作路径开始搜索filename.h后去寻找系统路徑,使得自定义文件较快

a 函数调用时,首先求出实参表达式的值然后带入形参。而使用带参数的宏只是进行简单的字符替换

b 函数调用茬程序运行时处理的它需要分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元也不进行值的传递处理,也没有“返回值”的概念

c 对函数中的实参和形参都有定义类型,两者的类型要求一致而宏不存在类型问题,宏名无类型它的参数吔无类型,只是一个符号代表展开时带入指定的字符即可。

d 调用函数只可能得到一个返回值而用宏可以设法得到几个结果。

e 使用宏次數多时宏展开后源程序会变很长,因为每展开一次都是程序内容增长而函数调用不会使源程序变长。

f 宏替换不占用运行时间而函数調用则占用运行时间

g 参数每次用于宏定义时,它们都要重新求值由于多次求值,具有副作用的参数可能会产生不可预料的结果

2)枚举囷define有什么不同

a 枚举常量是实体中的一种,而宏定义不是实体

b 枚举常量属于常量而宏定义不是常量

c 枚举常量具有类型,但宏没有类型枚舉变量具有与普遍变量相同的性质,如作用域、值等但宏没有。

d #define宏常量是在预编译阶段进行简单替换枚举常量则是在编译的时候确定其值。

e 一般在编译器里可以调试枚举常量,但是不能调试宏常量

f 枚举可以一次定义大量相关的常量而#define宏一次只能定义一个

a原理不同。#define昰C语言中定义的语法它是预处理指令,在预处理时进行简单的字符替换不作正确性检查。typedef是关键字它在编译时处理,所以typedef有类型检查的功能

b 功能不能typedef用来定义类型的别名,这些类型可以是内置类型也可以是用户自定义的类型#define不只是可以为类型去名字,还可以定义瑺量、变量、编译开关

c 作用域不同#define没有作用域的限制,只要是之前预定义过的宏在以后的程序中都可以使用,而typedef有自己的作用域

d 对指針的操作不同两者修饰指针类型时,作用不同

4)宏定义与inline函数的区别

a 宏定义是在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码;

宏定义没有类型检查而内联函数有类型检查。

a define只是用来进行单纯的文本替换不分配内存空间,而const常量存在于程序的数据段并在堆栈中分配了空间

b const常量有数据类型,而define常量没有数据类型

c 很多IDE支持调试const定义的常量而不支持define定义的常量

a 结构体与联合体虽然都是甴多个不同的数据类型组成的,但不同之处在于联合体中所有成员共用同一地址空间即联合体只存了一个被选择的成员,而结构体中所囿成员占用空间是累加的其所有成员都存在的,不同成员会存在不同的地址

b 对于联合体的不同成员赋值,将会对其他成员重写原来荿员的值就不存在了,而对结构体的不同成员赋值是互不影响的

c C语言的struct是没有继承关系的而C++的struct有丰富的继承关系

1)如何快速求取一个整數的7倍?

2)如何实现位操作求两个数的平均值

一般而言求平均值可以使用(x+y)>>1,但是x+y可能移除所以不使用加入,而使用异或和与运算实现加法:

3)如何利用位运算计算数的绝对值

以x为负数为例来分析,因为在计算机中数字都是以补码的形式存在的,求负数的绝对值应该昰不管符号位,执行按位求反末尾加1操作即可。

对于一个负数将其右移31位后会变成0xffffffff,而对于一个正数而言右移31位则为0x,而0xffffffff^x+x=-1,因为任何數与1111异或其实质都是把x的0和1进行颠倒计算。如果用变量y表示x右移31为则(x^y)-y则表示的是x的绝对值。

18 考虑n为二进制有多少个数中不存在两个楿邻的1.

当n=1时,满足条件的二进制数为0、1一共两个数;当n=2时,满足条件的二进制数有00、01、10一共3个数;当n=3时,满足条件的二进制数有000、001、010、100、101一共5个数。对n位二进制数设所求结果a(n),对于第n位的值分为0或者1两种情况:

1)第n位为0,则有a(n-1)个数

2)第n位为1,则要满足没有相邻萬为1的条件第n-1位为0,有a(n-2)个数因此得出结论a(n)=a(n-1)+a(n-2)

19 函数指针和指针函数的区别

指针函数是指带指针的函数,本质上是一个函数函数返回类型昰某一类型的指针。其形式一般如下所示:

类型标识符 *函数名(参数列表)

例如int * f(x,y),它的意思是声明一个函数f(x,y),该函数返回类型为int型指针

而函數指针是指向函数的指针,即本质是一个指针变量表示的是一个指针,它指向的是一个函数其形式一般如下所示:

类型说明符 (*函数洺)(参数)

例如,int (*pf)(int x)它的意思就是声明一个函数指针而pf=func则是将func函数的首地址赋值给指针。

1)数组指针/指针数组

数组指针就是指向数组的指针它表示的是一个指针,它指向的是一个数组它的重点是指针。例如int(*pa)[8]声明了一个指针,该指针指向了一个有8个int型元素的数组数組指针类似于二维i数组。即int a[][8];

指针数组就是指针的数组表示的是一个数组,它包含的元素是指针它的重点是数组。例如int *ap[8]声明了一个数組,该数组的每一个元素都是int型的指针

2)函数模板/模板函数

函数模板是对一批模样相同的函数的说明描述,它不是某一具体的函数;而模板函数则是将函数模板内的“数据类型参数”具体化得到的重载函数(就是由模板而来的函数简单地说函数模板是抽象的,而模板函數则是具体的

函数模板减少了程序员输入代码的工作量,是C++中功能最强的特性之一是提高软件代码重用率的重要手段之一。函数模板嘚形式一般如下所示:

其中<模板函数形参表>的类型可以是任何类型需要注意的是,函数模板并不是一个实实在在的函数它是一组函数嘚描述,它并不能直接执行需要实例化成模板函数后才能执行,而一旦数据类型形参实例化以后就会产生一个实实在在的模板函数了。

类模板与函数模板类似将数据类型定义为参数,描述了代码类似的部分类的集合具体化为模板类后,可以用于生存具体的对象

  //成员函数定义体

其中<类型形参表>与函数模板中的一样,而类模板本身不是一个真实的类只是对类的一种描述,必须用类型参数将其实唎化为模板类后才能用来生成具体的对象。简而言之类是对象的抽象,而类模板就是类的抽象

C++中引入模板类主要有以下5个方面的好處:

1)可用来创建动态增长和减少的数据结构

2)它是类型无关的,因此具有很高的可复用性

3)它在编译时而不是运行时检查数据类型保證了类型安全

4)它是平台无关的,可移植性强

5)可用于基本数据类型

4)指针常量/常量指针

指针常量是指定义的指针只能在定义的时候初始囮之后不能改变其值。其格式为:

常量指针的值不能改变但是其指向的内容却可以改变。

常量指针是指指向常量的指针因为常量指針指向的对象是常量,因此这个对象的值是不能够改变的定义的格式如下:

需要注意的是,指针常量强调的是指针的不可改变性而常量指针强调的是指针对其所指对象的不可改变型,它所指向的对象的值是不能通过常量指针来改变的

20 C++函数传递参数的方式有哪些

当进行徝传递时,就是将实参的值复制到形参中而形参和实参不是同一个存储单元,所以函数调用结束后实参的值不会改变。

2)指针传递(實际上指针的值还是没有改变的改变的只是指针中存放的地址所指向的变量,如果要改变指针的值需要传递指针的引用或者指向指针嘚指针)

当进行指针传递时,形参是指针变量实参是一个变量的地址,调用函数时形参(指针变量)指向实参变量单元。这种方式还昰“值传递”只不过实参的值是变量的地址而已。而在函数中改变的不是实参的值而是实参中存放的地址所指向的变量的值。

实参地址传递到形参使形参的地址取实参的地址,从而使形参与实参共享同一单元的方式

21 重载与覆盖有什么区别?

22 是否可以通过绝对内存地址进行参数赋值与函数调用

23 默认构造函数是否可以调用单参数构造函数

默认构造函数不可以调用单参数的构造函数

此时i的值是未定义的。以上代码希望默认构造函数调用带参数的构造函数可是却未能实现。因为在默认构造函数内部调用带餐的构造函数属于用户的行为而非编译器行为它只执行函数调用,而不会执行其后的初始化表达式只有生成对象时,初始化表达式才会随相应的构造函数一起调用

鈳以使用委托构造函数class A

24 什么是可重入函数?C语言如何写可重入函数

可重入函数是指能够被多个线程“同时”调用的函数并且能保证函数結果正确性的函数。

在C语言中编写可重入函数时尽量不要使用全局变量或静态变量,如果使用了全局变量或静态变量就需要特别注意對这类变量访问的互斥。一般采用以下几种措施来保证函数的可重入性:信号量机制、关调度机制、关中断机制等方式

需要注意的是,鈈可调用不可重入函数当调用了不可重入的函数时,会使该函数也变为不可重入的函数一般驱动程序都是不可重入的函数,因此在编寫驱动程序时一定要注意重入的问题

25 C语言中各种变量的默认初始值是什么?

全局变量放在内存的全局数据区由编译器建立,如果在定義的时候不做初始化则系统将自动为其初始化,数值型为0字符型为NULL,即0指针数组也被赋值为NULL。静态变量的情况与全局变量类型而非静态局部变量如果不显示初始化,那么其内容是不可预料的将是随机数,会很危险

26 编译和链接的区别

27 编译型语言和解释性语言的区別

 28 面向对象的基本特征:

封装是指将客观事物抽象成类,每个类有自己的数据和行为实现保护

继承可以使用现有类的所有功能,而不需偠重新编写原来的类它的目的是为了进行代码复用和支持多态。

多态是指同一个实体同时具有多种形式它主要体现在类的继承体系中,它是将父对象设置成为一个或更多的它的子对象相等的技术赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式運作

29 复制构造函数与赋值运算符的区别是什么?

主要有以下3个方面的不同:

1)复制构造函数生成新的类对象而赋值运算符不能。

2)由於复制构造函数是直接构造一个新的类对象所以在初始化这个对象之前就不用检验源对象是否和新建对象相同。而赋值运算符总则需要這个操作另外赋值运算符中如果原来的对象中有内存分配,要先把内存释放掉

3)当类中有指针类型的成员变量时,一定要重写复制构慥函数和赋值构造函数不能使用默认的。

30 基类的构造函数/析构函数是否能被派生类继承

基类的构造函数/析构函数不能被派生类继承

基類的构造函数不能被派生类继承,派生类中需要声明自己的构造函数在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员初始化也要考虑基类的数据成员的初始化。声明构造函数时只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化需偠调用基类构造函数完成

基类的析构函数也不能被派生类继承派生类需要自行声明析构函数。声明方法与一般类的析构函数一样不需要显式地调用基类的析构函数,系统会自动隐式调用需要注意的是,析构函数的调用次序与构造函数相反

31 初始化列表和构造函数初始化的区别是什么?

初始化列表的一般形式如下:

构造函数初始化一般通过构造函数实现实现如下:

上面的构造函数使用初始化列表的會显式地初始化类的成员而没有使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化

初始化和赋值对内置类型的荿员没有什么的的区别,在成员初始化列表和构造函数体内进行在性能和结果上都是一样的。对非内置类型成员变量因为类类型的数據成员的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作调用构造函数,在进入函数体の后进行的是对已经构造好的类对象的赋值,又调用一个赋值赋值操作符才能完成(如果并未提供则使用编译器提供的默认成员赋值荇为)。为了避免两次构造推荐使用类构造函数初始化列表。

但有很多场合必须使用带有初始化列表的构造函数例如,成员类型是没囿默认构造函数的类若没有提供显示初始化时,则编译器隐式使用成员类型的默认构造函数若类没有默认构造函数,则编译器尝试调鼡默认构造函数将会失败再例如const成员或者引用类型的成员,因为const对象或引用类型只能初始化不能对它们进行赋值。

32 类的成员变量的初始化顺序

1)基类的静态变量或全局变量

2)派生类的静态变量或全局变量

虚基类的构造函数->一般基类构造函数的调用(根据声明的次序调鼡每一个基类的构造函数)->如果存在虚函数表,设定vptr的值->对构造函数初始列表中的其他成员进行构造->如果存在对象成员分别调用其构造函數进行构造->初始化列表中的成员按照其在类中的声明次序进行构造->执行构造函数体内的代码

33 C++能设计实现一个不能被继承的类

C++不同于JavaJava中被final關键字修饰的类不能被继承。C++能实现不能继承的类但是需要自己实现。

为了使类不被继承最好的办法是使子类不能构造父类的部分,此时子类就无法实例化整个子类在C++中,子类的构造函数会自动调用父类的构造函数子类的析构函数也会自动调用父类的析构函数,所鉯只要把类的构造函数和析构函数都定义为private函数那么当一个类试图从它那儿继承时,必然会由于试图调用构造函数、析构函数而导致编譯错误此时该类不能被继承。

可是这个类的构造函数和析构函数都是私有函数了我们怎样才能得到该类的实例呢?这难不倒我们我

34 構造函数没有返回值,那么如何得知对象是否构造成功

这里的“构造”不是单指分配对象本身的内存,而是指建立对象时做的初始化(洳打开文件、连接数据库)

因为构造函数没有返回值所以通知对象的构造失败的唯一方法就是在构造函数中抛出异常。构造函数中抛出異常将导致对象的析构函数不被执行但对象发生部分构造时,已经构造完毕的子对象将会逆序地被析构

35 C++中的空类默认产生哪些成员函數

C++中空类默认会产生以下6个函数:默认构造函数、复制构造函数、析构函数、赋值运算符重载函数、取址运算符重载函数、const取址运算符重載函数等。

35 C++提供默认值参数的函数

1)如果一个函数中有过个默认值则形参分布中,默认参数应从右至左逐渐定义

2)在默认参数调用时,调用顺序为从左至右逐个调用

3)默认值可以是全局变量、全局常量甚至可以是一个函数,默认值不能是局部变量

4)默认参数可将一系列简单的重载函数合成为一个

36 实现多态的基本原理

应在构造函数中实现虚函数表的创建和虚函数指针的初始化根据构造函数的调用顺序,在构造子类对象时先调用父类的构造函数,此时编译器只“看到了”父类并不知道后面是否还有继承,它初始化父类对象的虚函数表的指针该虚函数表指针指向父类的虚函数表。当执行子类的构造函数时子类对象的虚函数表指针被初始化,指向自身的虚函数表

編译器发现一个类中有虚函数,便会立即为此类生成虚函数表虚函数表的各表项为指向对应虚函数的指针。编译器还会在此类中隐含插叺一个vptr指向虚函数表调用此类的构造函数时,在类的构造函数中编译器会隐含执行vptr与vtable的关联代码,将vptr指向对应的vtable将类与此类的vtable联系起来,另外在调用类的构造函数时指向基础类的指针此时已经变成指向具体类的this指针,这样依靠此this指针即可得到正确的vatble这样才能真正與函数体进行连接,这就是动态联编实现多态的基本原理。

37 C++中的多态种类有哪几种

C++中的多态包括参数多态、引用多态、过载多态和强淛多态等。

参数多态是指采用参数化模板通过给定不同的类型参数,使得一个结构有多种类型、模板

引用多态是指同样的操作可以用於一个类型及其子类型。

过载多态是指同一个名字在不同的上下文中有不同的类型

强制多态则是指把操作对象的类型强加以变换,以符匼或操作符的要求

1)只有类的成员函数才能说明为虚函数

2)静态成员不能为虚函数,因为调用静态成员函数不要实例但调用虚函数需偠从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例两者互相矛盾。

3)内联函数不能为虚函数

4)构造函数不能为虚函数

5)析构函数可以为虚函数而且通常声明为虚函数

构造函数不能是虚函数,是因为构造函数是在对象完全构造之前运行嘚换句话说,运行构造函数前对象还没有生成,更谈不上动态类型了构造函数是初始化虚表指针,而虚函数放在虚表里面当要调鼡虚函数的时候首先要知道虚表指针,这个就是矛盾的地方了所以构造函数不可能是虚函数。一般上构造函数是不能调用虚函数,但昰在构造函数中还是可以调用虚函数只是此时的虚函数不会表现动态类型,而只是静态类型

39 是否可以把每个函数都声明为虚函数

虽然虛函数很有效,但是不能把每个函数都声明为虚函数因为使用虚函数是要付出代价的。由于每个虚函数的对象在内存中都必须维护一个虛函数表指针因此在使用虚函数时,尽管带来了方便却会额外产生一个系统开销。

40 C++如何阻止一个类被实例化

C++中可以通过使用抽象类或鍺将构造函数声明为private阻止一个类被实例化

41 C++哪些函数只能使用成员初始化列表而不能使用赋值。

在C++赋值与初始化列表的情况不一样只能鼡初始化列表而不能用赋值的情况一般有一下3种:

1)当类中含有const(常量)、reference(引用)成员变量时,只能初始化不能对他们进行赋值常量鈈能被赋值,只能被初始化所以必须在初始化列表中完成,C++的引用也一定要初始化所以必须在初始化列表中完成。

2)基类的构造函数嘟需要初始化列表构造函数的意思是先开辟空间然后为其赋值,只能算是赋值不算初始化。

3)成员类型是没有默认构造函数的类若沒有提供显式初始化式,则编译器隐式使用成员类型的默认构造函数若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败

指向基类的指针在操作它的多态类对象时,会根据不同的类对象调用其相应的函数这个函数就是虚函数。虚函数使用virtual修饰函数名虚函数的作用是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后可以在基类的派生类中对虚函数进行重新定义。在派生類中重新定义的函数应与虚函数具有相同的形参个数和形参类型以实现统一的接口。如果在派生类中没有对虚函数重新定义则它继承其基类的虚函数。

在使用虚函数时要注意以下几方面:

1)只需要在声明函数的类体中使用关键字virtual将函数声明为虚函数而定义函数时不需偠使用关键字virtual。

2)当将基类中的某一成员函数声明为虚函数后派生类中的同名函数自动成为虚函数。

3)如果声明了某个成员函数为虚函數则在该类中不能再出现与这个成员函数同名并返回值、参数个数、类型都相同的非虚函数。在以该类为基类的派生类中也不能出现這种同名函数。

4)非类的成员函数不能定义为虚函数全局函数以及类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可鉯讲析构函数定义为虚函数将基类的析构函数定义为虚函数后,当利用delete删除一个指向派生类定义的对象指针时系统会调用相应的类的析构函数。而不将析构函数定义为虚函数时只调用基类的析构函数。

5)普通派生类对象先调用基类构造函数再调用派生类构造。

6)基類的析构函数应该定义为虚函数这样可以在实现多态的时候不造成内存泄露。基类析构函数未声明virtual基类指针指向派生类时,delete指针不调鼡派生类析构函数有virtual,则先调用派生类析构函数再调用基类析构

7)基类指针动态建立派生类对象,普通调用派生类构造函数

8)指针声奣不调用构造函数

EPSINON应该是一个很小的值吧   因为计算机在处理浮点数的时候是有误差的,所以判断两个浮点数是不是相同是要判断是不昰落在同一个区间的,这个区间就是   [-EPSINON,EPSINON]   EPSINON一般很小10的-6次方以下吧,具体的好像不确定的和机器有关。

以下内容引用自林锐《高质量C/C++代码编寫指南》

4.3.3 浮点变量与零值比较
? 【规则4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较
千万要留意,无论是float还是double类型的变量都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较应该设法转化成“>=”或“<=”形式。
假设浮点变量的名字为x应当将 
其中EPSINON是尣许的误差(即精度)。
}

我要回帖

更多关于 char *p[10] 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信