资源描述
网上流传的C+笔试题汇总1 .求下面函数的返冋值(微软) int func(x)(int countx = 0: while(x) (countx 卄;x = x&(x-l);|return countx:)假定x = 9999.答案:8思路:将x转化为2进制,看含有的1的个数。2 .什么是“引用” ?申明和使用“引用”要注意哪些问题?答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变敢直接操作效果完全相同。申明 个引用的时候,切记 要对其进行初始化。引用声明完毕后,相当于目标变盘名有两个名称,即该目标原名称和引用名,不能再把该弓I用名作为其 他变量名的别名。声明个引用,不是新定义了 ,个变量,它只表示该引用名是目标变量名的,个别名,它本身不是种数 据类型,因此引用本身不占存储単元,系统也不给引用分配存储单元.不能建立数组的引用.3,将“引用”作为函数参数有哪些特点?(1)传递引用给函数与传递指针的效果是样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对软的个别 名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作:而使用般変量传递函数的参数, 当发生函数调用时,需要给形参分配存储单元,形参变我是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。 因此当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重 更使用”指计变垣:名”的形式进行运算,这很容易产生错误且程序的阅读性较差:另一方面,在主调函数的调用点处,必须用 变量的地址作为实参。而引用更容易使用,更清晰.4,在什么时候需要使用“常引用” ?如果既要利用引用提高程序的效落乂要保护传递给函数的数据不在函数中被改变就应使用常引用。常引用声明方式;const 类型标识符&引用名=目标变量名:例1int a ;const int &ra=a:ra=l: 错误a=l; 正确例2string foo();void bar(string & s);那么下面的表达式将是非法的:bar(foo():bar (heHo world):原因在于5。()和hell。world”串都会产生一个临时对象,而在Ci中,这些临时对象都是const类型的。因此上面的表达 式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const .5,将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?格式:类型标识符&函数名(形参列表及类型说明)函数体好处:在内存中不产生被返回值的副本:(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着 该局部变量生存期的结束,相应的引用也会失效,产生runtime error!注意事项:1)不能返回局部变故的引用。这条可以参照Effective C+U的Item 31。主要原因是局部变量会在函数返回后被销毁, 因此被返冋的引用就成为了无所指的引用,程序会进入未知状态.2)不能返回函数内部new分配的内存的引用。这条可以参照EffectiveC+l的Item3L虽然不存在局部变量的被动销 毁问题,可对于这种情况(返冋函数内部new分配内存的引用),乂面临其它版尬局面。例如,被函数返冋的引用只是作为 个临时变量出现 而没有被赋个实际的变显 那么这个引用所指向的空间(由new分配)就无法料就造成memory leak3)可以返回类成员的引用,但最好是const.这条原则可以参照Effective C+l的Item 30。主要原因是当对象的属性 是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操 作封装在 业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业 务规则的完整性。4)流操作符重我返回值申明为“引用”的作用:流操作符和M这两个操作符常常希望被连续使用,例如:cout hello* endl:因此这两个操作符的返回值应该 是个仍然支持这两个操作符的流引用。可选的其它方案包括:返冋 个流对象和返冋个流对毂指针.但是对于返冋个 流对象,程序必须重新(拷贝)构造 个新的流对皴,也就是说,连续的两个操作符实际上是针对不同对皴的!这无法让 人接受。对于返冋个流指针则不能连续使用操作符。因此,返冋一个流对象引用是惟选择。这个唯选择很关键它 说明了引用的重要性以及无可替代性,也许这就是C+语彳中引入引用这个概念的原因吧。赋值操作符=。这个操作符象流操 作符样,是可以连续使用的,例如:x = j = 10;或者(x=10) = 100;赋值操作符的返冋値必须是一个左值,以便可以被继续 賦侑。因此引用成了这个操作符的惟一返冋值选择。例3# i ncludeint &put (int n);int vals10;int error=l:void mainO(put (0)-10; 以put (0)函数值作为左值,等价于vals0 = 10;put (9)=20; 以put (9)函数值作为左值,等价于vals9=20;coutint &put(int n)(if (n=0 & n=9 ) return valsn;else ( coul subscript error*; return error;()b: 输入a, b两变量的值swap(a,b): 接以变后a和b作为实参调用swap函数cduta , b; 输出结果)上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10.由【例2】可看出:(1传递引用给函数与传递指针的效果是样的。这时,被调函数的形参就成为原来上调函数中的实参变量或对象的 个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的冃标对象(在调函数中)的操作。(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作:而使用 般变量传递函数的参 数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本:如果传递的是对象,还将调用拷贝构造函 数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需 要重复使用、指针变量名”的形式进行运亢,这很容易产生错误且程序的阅读性较差:另 方面,在主调函数的调用点处,必 须用变量的地址作为实参。而引用更容易使用,更淸晰。如果既要利用引用提髙程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用.2、常引用常引用声明方式:const类型标识符&引用名=目标变量名:用这种方式声明的引用,不能通过弓I用对目标变量的值进行修改,从而使引用的目标成为const达到了引用的安全性。【例3】:int a ;const int &ra=a:ra=l: 错误a=li 正确这不光是让代码更健壮,也有些其它方面的需要。【例4】:假设有如下函数声明:string foo():void bar(string & s):那么卜面的表达式将是非法的:bar(foo():bar(hello world):原因在于foo()和hell。world中都会产生 个临时对象,而在C+中,这些临时对敦都是const类型的.因此上面的 表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。引用型参数应该在能被定义为const的情况下,尽量定义为const .3、引用作为返回值要以引用返冋函数值,则函数定义时要按以下格式:类型标识符&函数名(形参列表及类型说明)(函数体)说明:(1)以引用返冋函数值,定义函数时需要在函数名前加&(2)用引用返冋个函数值的最大好处是,在内存中不产生被返冋值的副本。【例5】以下程序中定义了 个普通的函数fnl (它用返回值的方法返回函数值),另外个函数fn2,它以引用的方法 返回函数值.include float tempi 定义全局变量tempfloat fnl (float r): 声明函数 fnlfloat &fn2(float r)J 声明函数 fn2float fnl (float r) 定义函数fnl,它以返回值的方法返回函数值(temp=(float)(r*r*3. 14);return temp;)float &fn2(float r) 定义函数62,它以引用方式返冋函数值(temp=(float)(r*r*3. 14);return temp;)void mainO 主函数(float a=fnl(10.0); /第1种情况,系统生成要返回值的副本(即临时变量)float &b=fnl(10.0); 第2种情况,可能会出错(不同C+系统有不同规定)不能从被调函数中返回一个临时变量或局部变量的引用float c=fn2(10.0); 第3种情况,系统不生成返冋值的副本可以从被调函数中返回一个全局变量的引用float 4d=fn2(10.0): 第4种情况,系统不生成返冋俏的副本可以从被调函数中返回一个全局变量的引用coutacd;)引用作为返回值,必须遵守以下规则:(1)不能返回局部变量的引用。这条可以参照EffectiveC+l的Item31 主要原因是局部变量会在函数返回后被销 毁,因此被返回的引用就成为了无所指”的引用,程序会进入未知状态。(2)不能返回函数内部new分配的内存的引用。这条可以参照EffectiveC+l的【tcm3l。虽然不存在局部变量的被 动销毁问题,可对丁这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是 作为 个临时变量出现,而没冇被赋予个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memuy leak。(3)可以返回类成员的引用,但最好是const。这条原则可以参照Effective C+l的Item 30.主要原因是当对象的 属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋 值操作封装在个业务规则当中.如果其它対象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破 坏业务规则的完整性。(4)引用与一些操作符的重载:流操作符和,这两个操作符常常希望被连续使用.例如:cout hell。” endl:因此这两个操作符的返回值 应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回 个流对象,程序必须重新(拷贝)构造 个新的流对象,也就是说,连续的两个操作符实际上是针对不同对象的!这无 法让人接受。对于返回个流指针则不能连续使用操作符。因此,返回一个流对象引用是惟选择。这个唯选择很关键, 它说明引用的重要性以及无可替代性,也许这就是Cx语洋中引入引用这个概念的原因吧。赋俏操作符=。这个操作符銀 流操作符样,是可以连续使用的,例如:x - j - 10;或者(x=10)=100;赋值操作符的返回值必须是个左值,以便可以被 继续赋位。因此引用成r这个操作符的惟一返国值选择。【例6测试用返回引用的函数值作为除值表达式的左值。include int &put(int n);int vals10;int error=l:void mainO(put (0) = 10; 以put (0)函数值作为左值,等价于vals0 = 10;put (9)=20; 以put (9)函数值作为左值,等价于vals9=20;coutvals0;cout=0 & n ( expression ) converts expression to the type of type-id based solely on the types present in the expression. No run-time type check is made to ensure the safety of the conversion. Syntaxstatic_cast ( expression )The static cast operator can be used for operations such as converting a pointer to a base class to a pointerto a derived class. Such conversions are not always safe. For example:class B . ;class D : public B ( . J : void f(B* pb, D* pd)(D* pd2 = static_cast(pb);/ not safe, pb may/ point to just B/ safe conversionB* pb2 - static_castB*Xpd);In contrast to dynamic cast, no run-time check is made on the static cast conversion of pb. The object pointed to by pb may not be an object of type D, in which case the use of tpd2 could be disastrous. For instance, calling a function that is a member of the D class, but not the B class, could result in an access violatiwi.The dynamic_cast and static_cast operators move a pointer throughout a class hierarchy. However, static_cast relies exclusively on the informat ion provided in the cast statement and can therefore be unsafe. For example: class B ( . );class D : public B . : void f(B* pb) (D* pd! 二 dynamic_castD*Xpb);D* pd2 = slat ic_caslD*Xpb);If pb really points to an object of type D, then pdl and pd2 will get the same value. They will also get the same value if pb = 0.If pb points to an object of type B and not to the complete D class, then dynamic_cast will know enough to return zero. However, static_cast relies on the programmer s assertion that pb points to an object of type D and simply returns a pointer to that supposed D object.Consequently, static_cast can do the inverse of implicit conversions, in which case the results are undefined. It is left to the programmer to ensure that the results of a static_cast conversion are safe.This behavior also applies to types other than class types. For instance, static_cast can be used to convert from an int to a char. However, the resulting char may not have enough bits to hold the entire int value. Again, it is left to the programmer to ensure that the results of a static_cast conversion are safe.The static_cast operator can also be used to perform any implicit conversion, including standard conversions and user-defined conversions. For example: typedef unsigned char BYTE void f() (char ch;int i = 65;float f = 2. 5;double dbl;ch = static_cast(i);/ int to chardbl = static_cast(f);/ float to doublei = static_cast(ch);The static cast operator can explicitly convert an integral value to an enumeration type. If the value of the integral type does not fal 1 within the range of enumeration values, the resulting enumeration value is undefined. The static cast operator converts a nul1 pointer value to the nul1 pointer value of the destinat ion type.Any expression can be explicitly converted to type void by the statijeast operator. The destination void type can optionally include the const, volati1e, or _unaligned attribute.The static_cast operator cannot cast away the const, volatile, or _unaligned attributes.statijeast在功能上基本上与C风格的类型转换样强大,含义也样。它也有功能上限制.例如,你不能用statijeast 象用C风格的类型转换一样把struct转换成int类型或者把double类型转换成指针类型,另外,statijeast不能从表达 式中去除const属性,因为另 个新的类型转换操作符const_cast冇这样的功能。2、 const_cast OperatorMSDN:The const cast operator can be used to remove the const, volatile, and unaligned attribute(s) from a class. Syntax const cast lype-id ( expression )A pointer to any object type or a pointer to a data member can be explicitly converted to a type that is identical except for the const, volatile, and _unaligned qualifiers. For pointers and references, the result wi11 refer to the original object. For pointers to data members, the result will refer to the same member as the original (uncast) pointer to data member. Depending on the type of the referenced object, a wri te operation through the resulting pointer, reference, or pointer to data member might produce undefined behavior.The const_cast operator converts a null pointer value to the null pointer value of the destination type, const cast用于类型转换掉表达式的const或volati leness属性。通过使用const cast你向人们和编译器强调你通过类 型转换想做的只是改变 些东西的constness或者volatileness属性。这个含义被编译器所约束。如果你试图使用 const cast来完成修改constncss或者volati leness属性之外的事情,你的类型转换将被拒绝。3、 dynamic cast OperatorMSDN:The expression dynamic casttype-id( expression ) converts the operand expression to an object of type type-id. The type-id must be a pointer or a reference to a previously defined class type or a *pointer to void” . The type of expression must be a pointer if type-id is a pointer, or an 1-value if type-id is a reference.Syntaxdynamic_cast ( expression )If type-id is a pointer to an unambiguous accessible direct or indirect base class of expression, a pointer to the unique subobject of type type-id is the result. For example:class B ( . );class C : public B . );class D : public C . ;void f(D* pd)MC* pc - dynamic_cast(pd):/ ok: C is a direct base class/ pc points to C subobject of pdB* pb = dynamic_cast(pd):/ ok: B is an indirect base class/ pb points to B subobject of pdBThis type of conversion is cal led an “upcast because it moves a pointer up a class hierarchy, from a derived class to a class it is derived from. An upcast is an implicit conversion.If type-id is void*, a run-time check is made to determine the actual type of expression. The result is a pointer to the complete object pointed to by expression. For example: class A ;class B f .);void f0(A* pa = new A:B* pb = new B;void* pv = dynamic_cast(pa):/ pv now points to an object of type Apv = dynamic_castvoid*Xpb):/ pv now points to an object of type BIf type-id is not void*, a run-time check is made to see if the object pointed to by expression can be convertedto the type pointed to by type-id.If the type of expression is a base class of the type of type-id, a run-time check is made to see if expression actually points to a complete object of the type of type-id. If this is true, the result is a pointer to a complete object of the type of type-id. For example: class B );class D : public B . :void f()(B* pb = new D;/ unclear but okB* pb2 = new B;1)* pd = dynamic_cast(pb);/ ok: pb actual ly points to a DD* pd2 = dynamic_cast(pb2);/error: pb2 points to a B, not a D/ pd2 = NULLThis type of conversion is called a MdowncastM because it moves a pointer down a class hierarchy, from a given class to a class derived from it.dynamijcast,它被用于安全地沿着类的继承关系向进行类型转换。这就是说,你能用dynami jcast把指向基类的指针或 引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行 类型转换时)或者抛出异常(当对引用进行类型转换时).dynamijcasts在帮助你浏览继承层次I.是冇限制的。它不能被用于缺乏虚函数的类型匕也不能用它来转换掉constness。4、 reinterpret_cast OperatorMSDN:The reinterpret cast operator al lows any pointer to be converted into any other pointer type. It also al lows any integral type to be converted into any pointer type and vice versa. Misuse of the reinterpret_cast operator can easi1y be unsafe. Unless the desired conversion is inherently low-level, you shou1d use one of the other cast operators.Syntaxreinterpret_cast ( expression )The reinterpret cast operator can be used for conversions such as char* to int*, or One class* to Unrelated class*, which are inherently unsafe.The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable.The reinterpret_cast operator cannot cast away the const, volatile, or _unaligned attributes.使用这个操作符的类型转换,其的转换结果几乎都是执行期定义(implementation-defined)。因此,使用reinterpret casts 的代码很难移植。reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换,比如转换函数指针的代码是不可移植的(C+不保证所冇的函数指针都被用样的方法表示),在些情况下这样的转换会产 生不正确的结果(参见条款M31),所以你应该避免转换函数指针类型.5、如果你使用的编译器缺乏对新的类型转换方式的支持,你可以用传统的类型转换方法代替static_cast, const_cast,以 及reinlerpret cast,也可以用下面的宏替换来模拟新的类型转换语法:加efine static_cast(TYPE, EXPR)(TYPE)(EXPR)define const cast (TYPE, EXPR)(TYPE) (EXPR)ddefine reinterpret.cast(TYPE, EXPR) (TYPE)(EXPR)这些模拟不会象真实的操作符样安全,但是当你的编译器可以支持新的的类型转换时,它们可以简化你把代再升级的过程。没仃 个容易的方法来模拟dynamijeasi的操作,但是很多函数库提供了函数,安全地在派生类与基类之间进行类型转换。 如果你没有这些函数而你有必须进行这样的类型转换,你也可以回到C风格的类型转换方法上,但是这样的话你将不能获知 类型转换是否失败.当然,你也可以定义个宏来模拟dynamijeast的功能,就象模拟其它的类型转换样: ttdefine dynamic.cast(TYPE, EXPR) (TYPE)(EXPR)请记住,这个模拟并不能完全实现dynamijeast的功能,它没有办法知道转换是否失败。垂点是stat ic_cast, dynaraic_cast和 reinterpret_cast 的区别和应用.|31.分别写出BOOL int,float,指针类型的变量a与“零”的比较语句.答案:BOOL : if ( !a ) or if(a) int : if ( a = 0) float : const EXPRESSION EXP = 0. 000001 if ( a EXP) pointer : if ( a != NULL) or if(a = NULL) 32.请说出const与#define相比,有何优点? 答案:1) const常量冇数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检査。而对后者只进行字符替 换,没冇类型安全检位,并且在字符替换可能会产生意料不到的错误。2)有些集成化的调试工具可以对consl常敢进行调试,但是不能对宏常证进行词试。33.简述数组与指针的区别?数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。 (1)修改内容上的差别 char a = hello” : ao =;char *p = world” ; 注意p指向常量字符串 p0 = X ; /编译器不能发现该借误,运行时错误 (2)用运算符sizeof可以计算出数组的容量(字节数)。sizeof(p),p为指针得到的是个指针变量的字节数,而不是p所 指的内存容量。Ci/C语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行 传递时,该数组自动退化为同类型的指针。char a( = hello world;char *p = a;cout sizeof (a) cndl; / 12 字节 cout sizeof (p) cndl; / 4 字节 计算数组和指针的内存容量 void Func(char a100) (cout sizeof(a) endl; / 4 字节而不是 100 字节 ?弘.类成员函数的重藏、覆盖和也戳区别? 答案:&成员函数被重载的特征:(1)相同的范阳(在同个类中);(2)函数名字相同:(3)参数不同:(4) virtua!关键字可有可无。b.田説是指派生类函数徴前基类函数,特征是: (1)不同的范围(分别位于派生类与基类); (2)函数名字相同:(3)参数相同:(4)基类函数必须有virtual关键字.c. “隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如F:(1)如果派生类的函数与基类的函数同名,但
展开阅读全文