选择题改错

 

 

. 单选

1.2.

以下程序的运行结果是()

int main(void)

{

printf("%s , %5.3s\n","computer","computer");

return 0;

}

A A.computer , puter 

   B.computer , com

   C.computer ,computer

   D.computer , compu.ter

正确答案:B

解析:%5.3s表示输出栈5列,但是只取字符串中左端3个字符,这3个字符输出在5列的右侧,左端补空格

6.7

关于"深拷贝",下列说法正确的是()

   A.会拷贝成员数据的值和会拷贝静态分配的成员对象

B B.只会拷贝成员数据的值

C C.只会拷贝静态分配的成员对象

D D.只会拷贝动态分配的成员对象

正确答案:A

5.3

下列程序的打印结果是?

char p1[15]= "abcd",*p2= "ABCD", str[50]= "xyz";

strcpy(str+2,strcat(p1+2,p2+1));

printf("%s",str);

A A.xyabcAB

B B.abcABz

C C. ABabcz

D D.xycdBCD

    E.运行出错

正确答案:D .注意strcat(p1+2,p2+1)是将BCD拼接在abcd的后面,然后返回到c的位置

5.6

STL中的unordered_mappriority_queue使用的底层数据结构分别是什么?()

   A. rbtree,queue

B B.hashtable,heap

C C. rbtree,heap

D D.hashtable,queue

正确答案:B

5.7

下面说法正确的是()

   A. 一个空类默认一定生成构造函数,拷贝构造函数,赋值操作符,引用操作符,析构函数

B B.可以有多个析构函数

C C. 析构函数可以为virtual,可以被重载

D D.类的构造函数如果都不是public访问属性,则类的实例无法创建

对于B,类的构造函数一般是共有的(public),但有时也把构造函数声明为私有的(private),其作用是限制其创建该类对象的范围,这时,只能在本类和友元中创建该类对象。如单例模式。

5.10

请选择下列程序的运行结果

#include

using namespace std;

class B0//基类BO声明

{

public://外部接口

virtual void display()//虚成员函数

{

cout<<"B0::display0"<

};

class B1:public B0//公有派生

{

public:

void display() { cout<<"B1::display0"<

};

class D1: public B1//公有派生

{

public:

void display(){ cout<<"D1::display0"<

};

void fun(B0 ptr)//普通函数

{

ptr.display();

}

int main()//主函数

{

B0 b0;//声明基类对象和指针

B1 b1;//声明派生类对象

D1 d1;//声明派生类对象

fun(b0);//调用基类B0函数成员

fun(b1);//调用派生类B1函数成员

fun(d1);//调用派生类D1函数成员

}

  A.B0::display0 B0::display0 B0::display0

BB. B0::display0 B0::display0 D1::display0

  C.B0::display0 B1::display0 D1::display0

DD. B0::display0 B1::display0 B1::display0

正确答案:A

没有构成多态,与类型有关,因此调用的都是B0的函数

11.9

以下程序输出结果是____。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

class A

{

public:

    virtual void func(int val = 1)

    { std::cout<<"A->"<

    virtual void test()

    { func();}

};

class B : public A

{

public:

    void func(int val=0)

{std::cout<<"B->"<

};

int main(int argc ,char* argv[])

{

    B*p = new B;

    p->test();

return 0;

}

A.A->0
B.B->1
C.A->1
D.B->0
E.编译出错
F.以上都不对
分析:
正确答案:
Bvirtual 函数是动态绑定,而缺省参数值却是静态绑定。 意思是你可能会 在“调用一个定义于派生类内的virtual函数”的同时,却使用基类为它所指定的缺省参数值。

结论:绝不重新定义继承而来的缺省参数值!(可参考《Effective C++》条款37

对于本例:

1

2

3

B*p = newB;

 

p->test();

p->test()执行过程理解

       (1) 由于B类中没有覆盖(重写)基类中的虚函数test(),因此会调用基类A中的test()

       (2) A中test()函数中继续调用虚函数 fun(),因为虚函数执行动态绑定,p此时的动态类型(即目前所指对象的类型)为B*,因此此时调用虚函数fun()时,执行的是B类中的fun();所以先输出“B->”;

       (3) 缺省参数值是静态绑定,即此时val的值使用的是基类A中的缺省参数值,其值在编译阶段已经绑定,值为1,所以输出“1”;

       最终输出“B->1”。所以大家还是记住上述结论:绝不重新定义继承而来的缺省参数值!

10.2

选择题改错_第1张图片

分析:

假设每行有n个元素:

[9][9] - [4][4] = 21c-140=5n+5

[7][7] - [4][4]=x- 140 =3n+3

[7][7] = 140+3/5*( 21c-140 )= 1c4

10.7

选择题改错_第2张图片

10.8

10.选择题改错_第3张图片

分析:new一个子类对象时先调用父类的默认构造函数

10.10

选择题改错_第4张图片

9.5

选择题改错_第5张图片

8.10

选择题改错_第6张图片

B 继承自 A, 先 A 构造函数,输出 0 ,然后 B 构造函数, B 的 test 继承自 A ,然后找 func 函数找到 B 自己的,所以输出 1 ,然后 p->test() 再执行一次,输出 2

另外注意:在《C++ Primer》556页中讨论了构造函数和析构函数中调用虚函数的效应,得出的结果是:在构造或析构函数中调用虚函数会执行与之所属类型相对应的虚函数版本。

注意:
绝不在构造和析构过程中调用virtual方法(包括直接调用和间接调用),为啥? 原因很简单,对于前者,这种情况下,子类专有成分还没有构造,对于后者,子类专有成分已经销毁,因此调用的并不是子类重写的方法,这不是程序员所期望的。

8.2

选择题改错_第7张图片

分析:

题意为输入设定全部是大写(ASCII码A-Z为65-90,递增),所以有两种情况:

一、count[0;25]存储A-Z的个数,即count[0]存储A的个数,于是(1)++count[a[i]-'A'];(2)'A'+i,count[i];

二、count[0;25]存储Z-A的个数,即count[0]存储Z的个数,于是(1)++count['Z'-a[i]];(2)'Z'-i,count[i]。

所以答案为D。

8.5

选择题改错_第8张图片

分析:if条件中,前面的为真,c--没有执行(短路特性)

选择题改错_第9张图片

分析:
因为+运算符被定义为为成员函数,也就是说要想调用该成员函数,+左侧的运算对象必须是本类的类型。换一种等价写法会一目了然:
3+3;调用内置类型的+运算符,和本题重载+运算符无关;
b1+3;等价于b1.operator+(3);成立
b1+b2;等价于b1.operator+(b2);成立
3+b1;等价于3.operator+(b1);显然3是一种内置类型,根本没有成员函数;

选择题改错_第10张图片

分析:this指针是const的,无法修改其指向,没有这句话确实会栈溢出,但是现在编译都会出错

选择题改错_第11张图片

0x是十六进制,后面的每个数字是4位,即两个数字构成一个字节。另外小端、结构体内存对齐

选择题改错_第12张图片

答案A。

重载函数中使用默认参数是允许的,但在调用函数时要小心出现如下二义性。

1

2

void foo();

void foo(int a=0);

 

5.

在上述函数声明下,如果我们在程序中这样调用foo()函数时,就会出现二义性,此时无法通过编译阶段。

下面代码不能正确输出hello的选项为

#include

struct str_t{

long long len;

char data[32];

};

struct data1_t{

long long len;

int data[2];

};

struct data2_t{

long long len;

char *data[1];

};

struct data3_t{

long long len;

void *data[];

};

int main(void)

{

struct str_t str;

memset((void*)&str,0,sizeof(struct str_t));

str.len=sizeof(struct str_t)-sizeof(int);

snprintf(str.data,str.len,"hello");//VS下为_snprintf

____________________________________;

____________________________________;

return 0;

}

选择题改错_第13张图片

b中把'h'强转为char*了,而不是它的地址。这里面的关键是:  “取这个变量的地址”  和  “把变量的值当作地址”  的区别。这题应该  “取变量的地址。

7.

关于以下代码,哪个说法是正确的?

myClass::foo(){

delete this;

}

..

void func(){

myClass *a = new myClass();

a->foo();

}

选择题改错_第14张图片

分析:

在类的成员函数中能不能调用delete this?答案是肯定的,能调用,而且很多老一点的库都有这种代码。假设这个成员函数名字叫release,而delete this就在这个release方法中被调用,那么这个对象在调用release方法后,还能进行其他操作,如调用该对象的其他方法么?答案仍然是肯定 的,调用release之后还能调用其他的方法,但是有个前提:被调用的方法不涉及这个对象的数据成员和虚函数。说到这里,相信大家都能明白为什么会这样 了。

根本原因在于delete操作符的功能和类对象的内存模型。当一个类对象声明时,系统会为其分配内存空间。在类对象的内存空间中,只有数据成员和虚函数表指针,并不包含代码内容,类的成员函数单独放在代码段中。在调用成员函数时,隐含传递一个this指针,让成员函数知道当前是哪个对象在调用它。当 调用delete this时,类对象的内存空间被释放。在delete this之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。

为什么是不可预期的问题?delete this之后不是释放了类对象的内存空间了么,那么这段内存应该已经还给系统,不再属于这个进程。照这个逻辑来看,应该发生指针错误,无访问权限之类的令系统崩溃的问题才对啊?这个问题牵涉到操作系统的内存管理策略delete this释放了类对象的内存空间,但是内存空间却并不是马上被回收到系统中,可能是缓冲或者其他什么原因,导致这段内存空间暂时并没有被系统收回。此时这段内存是可以访问的,你可以加上100,加上200,但是其中的值却是不确定的。当你获取数据成员,可能得到的是一串很长的未初始化的随机数;访问虚函数表,指针无效的可能性非常高,造成系统崩溃。


大致明白在成员函数中调用delete this会发生什么之后,再来看看另一个问题,如果在类的析构函数中调用delete this,会发生什么?实验告诉我们,会导致堆栈溢出。原因很简单,delete的本质是“为将被释放的内存调用一个或多个析构函数,然后,释放内存” (来自effective c++)。显然,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃

上面是某大牛的分析,而在实际的运行过程中使用delele this确实会直接出现错误。这是因为:在成员函数中调用delete this,首先会调用类的析构函数,this指针已删除,会出现指针错误。

下面是在XCode中使用delete this出现的错误:

malloc: *** error for object 0xbffffa18: pointer being freed was not allocated

//注意0xbffffa18即为this的地址
*** set a breakpoint in malloc_error_break to debug

而在VS2010中使用delete this是直接导致 Debug Assertion Failed!

具体的描述是:invalid null pointer

总结:在成员函数中调用delete this,会导致指针错误,而在析构函数中调用delete this,出导致死循环,造成堆栈溢出。

PS:this是类中成员函数具有的一个附加的隐含形参,即指向该类对象的一个指针,它与调用成员函数的对象绑定在一起。同时1.在普通的非const成员函数中:this的类型是一个指向类类型的const指针,可以改变this指向的值,但是不能改变this所保存的地址;2.在const成员函数中,this的类型是一个指向const类类型对象的const指针,既不能改变this所指向的对象,也不能改变this所保存的地址。

注意:

  1. 成员函数中不能定义this形参,而是由编译器隐含地定义,但是可以在成员函数中显示使用this形参,不过也不是必须这么做。如果对类成员的引用没有限定,编译器会将这种引用处理成通过this指针的引用。
  2. 有一种情况下必须显式使用this:当我们需要将一个对象作为整体引用而不是引用对象的一个成员时。
  3. 从const成员函数返回*this:不能从const成员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const引用。

6.

选择题改错_第15张图片

分析:

选择题改错_第16张图片

 

 

你可能感兴趣的:(易错题)