一、单选题(20题,每题2分) 1、有变量int i = 0; int a = i++; int b=++a; int c = a+b;请问表达式 a?b:c的值是() A、0 B、1 C、2 D、3 2、32位环境下,int *p=new int[10];请问sizeof(p)的值为() A、4 B、10 C、40 D、8 3、有语句char str[] = "abcde";请问表达式sizeof(str)的值是() 后面还有\0 A、1 B、4 C、5 D、6 4、有函数int func(int i)的实现为() [cpp] view plaincopy
1. int func(int i)
2. {
3. if(i > 1)
4. return i*func(i-1);
5. else
6. return 1;
7. }
请问函数调用f(5)的返回值是多少()阶乘计算 A、5 B、15 C、20 D、120 5、请问以下说法,哪个是正确的() A、每个类都有一个无参数的构造函数 B、每个类都有一个拷贝构造函数 C、每个类能有多个构造函数 D、每个类能有多个析构函数 6、用class关键字定义的类,其成员默认的访问属性为() A、private B、protected C、public D、无定义 7、类的成员有三种访问属性,分别是public、protected、private,子类能够访问的成员是() A、都能访问 B、public和protected C、public和private D、protected和private 8、请问对一个排好序的数组进行查找,时间复杂度为() A、O(n) B、O(lgn) C、O(nlgn) D、O(1) 9、以下二叉树:
后序遍历的结果是() A、丙乙丁甲戊己 B、甲乙丙丁戊己 C、丙丁乙己戊甲 D、丙丁己乙戊甲 10、看以下代码: A *pa = new A[10]; delete pa; 则类A的构造函数和析构函数分别执行了几次() A、1 1 B、10 10 C、1 10 D、10 1 11、看以下代码: [cpp] view plaincopy
1. class A
2. {
3. public:
4. ~A();
5. };
6. A::~A()
7. {
8. printf("delete A ");
9. }
10.
11. class B : public A
12. {
13. public:
14. ~B();
15. };
16. B::~B()
17. {
18. printf("delete B ");
19. }
请问执行以下代码 A *pa = new B(); delete pa; 输出的串是()A A、delete A B、delete B C、delete B delete A D、delete A delete B 注: 构造函数与析构函数的调用顺序 构造函数 先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用问题都能解决;构造函数的调用顺序总是如下: 1.基类构造函数。如果有多个基类,则构造函数的调用顺序是某类在类派生表中出现的顺序,而不是它们在成员初始化表中的顺序。 2.成员类对象构造函数。如果有多个成员类对象则构造函数的调用顺序是对象在类中被声明的顺序,而不是它们出现在成员初始化表中的顺序。 3.派生类构造函数。 析构函数 析构函数的调用顺序与构造函数的调用顺序正好相反,将上面3个点反过来用就可以了,首先调用派生类的析构函数;其次再调用成员类对象的析构函数;最后调用基类的析构函数。 析构函数在下边3种情况时被调用: 1.对象生命周期结束,被销毁时(一般类成员的指针变量与引用都i不自动调用析构函数); 2.delete指向对象的指针时,或delete指向对象的基类类型指针,而其基类虚构函数是虚函数时; 3.对象i是对象o的成员,o的析构函数被调用时,对象i的析构函数也被调用。 下面用例子来说说构造函数的的调用顺序: #include "stdafx.h" #include "iostream" using namespace std; class Base { public: Base(){ std::cout<<"Base::Base()"<<std::endl; } ~Base(){ std::cout<<"Base::~Base()"<<std::endl; } }; class Base1:public Base { public: Base1(){ std::cout<<"Base1::Base1()"<<std::endl; } ~Base1(){ std::cout<<"Base1::~Base1()"<<std::endl; } }; class Derive { public: Derive(){ std::cout<<"Derive::Derive()"<<std::endl; } ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; } }; class Derive1:public Base1 { private: Derive m_derive; public: Derive1(){ std::cout<<"Derive1::Derive1()"<<std::endl; } ~Derive1(){ std::cout<<"Derive1::~Derive1()"<<std::endl; } }; int _tmain(int argc, _TCHAR* argv[]) { Derive1 derive; return 0; } 运行结果是: Base::Base() Base1::Base1() Derive::Derive() Derive1::Derive1() Derive1::~Derive1() Derive::~Derive() Base1::~Base1() Base::~Base() 那么根据上面的输出结果,笔者稍微进行一下讲解,构造函数的调用顺序是;首先,如果存在基类,那么先调用基类的构造函数,如果基类的构造函数中仍然存在基类,那么程序会继续进行向上查找,直到找到它最早的基类进行初始化;如上例中类Derive1,继承于类Base与Base1;其次,如果所调用的类中定义的时候存在着对象被声明,那么在基类的构造函数调用完成以后,再调用对象的构造函数,如上例中在类Derive1中声明的对象Derive m_derive;最后,将调用派生类的构造函数,如上例最后调用的是Derive1类的构造函数。 virtual析构函数 下面来说一说为多态基类声明virtual析构函数: 在C++中,构造函数不能声时为虚函数,这是因为编译器在构造对象时,必须知道确切类型,才能正确的生成对象,因此,不允许使用动态束定;其次,在构造函数执行之前,对象并不存在,无法使用指向此此对象的指针来调用构造函数,然而,析构函数是可以声明为虚函数;C++明白指出,当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义---实际执行时通常发生的是对象的derived成分没被销毁掉。 看下面的例子: class Base { public: Base(){ std::cout<<"Base::Base()"<<std::endl; } ~Base(){ std::cout<<"Base::~Base()"<<std::endl; } }; class Derive:public Base { public: Derive(){ std::cout<<"Derive::Derive()"<<std::endl; } ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; } }; int _tmain(int argc, _TCHAR* argv[]) { Base* pBase = new Derive(); //这种base classed的设计目的是为了用来"通过base class接口处理derived class对象" delete pBase; return 0; } 输出的结果是: Base::Base() Derive::Derive() Base::~Base() 从上面的输出结果可以看出,析构函数的调用结果是存在问题的,也就是说析构函数只作了局部销毁工作,这可能形成资源泄漏败坏数据结构等问题;那么解决此问题的方法很简单,给base class一个virtual析构函数; class Base { public: Base(){ std::cout<<"Base::Base()"<<std::endl; } virtual ~Base(){ std::cout<<"Base::~Base()"<<std::endl; } }; class Derive:public Base { public: Derive(){ std::cout<<"Derive::Derive()"<<std::endl; } ~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; } }; int _tmain(int argc, _TCHAR* argv[]) { Base* pBase = new Derive(); delete pBase; return 0; } 输出结果是: Base::Base() Derive::Derive() Derive::~Derive() Base::~Base() 可能上面的输出结果正是我们所希望的吧,呵呵!由此还可以看出虚函数还是多态的基础,在C++中没有虚函数就无法实现多态特性;因为不声明为虚函数就不能实现“动态联编”,所以也就不能实现多态啦! 12、文件长度是一个大于0的整数,用变量unsigned file_length; 来表示,把文件分成块,每块的长度也是一个大于0的整数,用变量unsigned block_length; 来表示,则文件被分成的块数为() A、file_length/block_length B、file_length/block_length+1 C、(file_length+block_length-1)/block_length D、((file_length-1)/block_length+1 13、整数int i = 0xFE78DA45; int k = 0xAC3189B2;则i^k的值为() A、0x524953f7 B、0xAC308800 C、0xFE79DBF7 D、0X0000001 注意:^是位异或符号 14、看以下代码: [cpp] view plaincopy
1. class parent
2. {
3. public:
4. virtual void output();
5. };
6. void parent::output()
7. {
8. printf("parent!");
9. }
10.
11. class son : public parent
12. {
13. public:
14. virtual void output();
15. };
16. void son::output()
17. {
18. printf("son!");
19. }
则以下程序段: son s; ::memset(&s , 0 , sizeof(s)); parent& p = s; p.output(); 执行结果是() A、parent! B、son! C、son!parent! D、没有输出结果,程序运行出错 C函数之memset()函数用法 1.功能:将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为指向S的指针 2. 需要的头文件 <memory.h> or <string.h> 3.函数原型: void *memset(void *s,int ch, unsigned n) 4.memset可以方便的清空一个结构类型的变量或数组(在程序设计中的主要用法) 如: struct sample_struct { char csName[16]; int iSeq; int iType; }; 对于变量 struct sample_strcut stTest; 一般情况下,清空stTest的方法: stTest.csName[0]='\0'; stTest.iSeq=0; stTest.iType=0; 用memset就非常方便: memset(&stTest,0,sizeof(struct sample_struct)); 如果是数组: struct sample_struct TEST[10]; 则 memset(TEST,0,sizeof(struct sample_struct)*10); 5.memset函数其他用法 Ø void *memset(void *s,int c,size_t n) 总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 Ø memset() 函数常用于内存空间初始化(如上面4所说)。 如: char str[100]; memset(str,0,100); Ø memset()的深刻内涵:用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘memset(a, '\0', sizeof(a)); 15、函数的局部变量所需存储空间,是在哪里分配的() A、进程的数据段 B、进程的栈上 C、进程的堆上 D、以上都可以 16、以下STL的容器存放的数据,哪个肯定是排好序的() A、vector B、deque C、list D、map 17、int a[][3]={{1},{3,2},{6,7,8},{9}};中a[2][1]的值是() A、3 B、6 C、2 D、7 18、以下关于头文件,说法正确的是() A、#include<filename.h>,编译器寻找头文件时,会从当前编译的源文件所在的目录去找 B、#include“filename.h”,编译器寻找头文件时,会从通过编译选项指定的目录去找 C、多个源文件同时用到的全局整数变量,它的声明和定义都放在头文件中,是好的编程习惯 D、在大型项目开发中,把所有自定义的数据类型、全局变量、函数声明都放在一个头文件中,各个源文件都只需要包含这个头文件即可,省去了要写很多#include语句的麻烦,是好的编程习惯。 19、某棵完全二叉树上有699个节点,则该二叉树的叶子节点数为() A、349 B、350 C、188 D、187 n0=n2+1; n=n0+n1+n2=n0+n1+n0-1=699 由于完全二叉树中度为1的节点只有0个或1个两种情况,所以,将0或1带入上面公式,整理后得: n0=(n+1)/2或者n0=n/2; 看看n是否能被2整除,能则用n0=n/2。否则用n0=(n+1)/2既叶子节点为n0=(n+1)/2=350 20、在一个指向字符串的指针char *p_str,要把字符串中第4个字符的值改为'a',正确的做法是() A、p_str[3]='a' B、*(ptr+3)='a' C、p_str[4]='a' D、*(ptr+4)='a' 二、多选题(10题,每题3分,错选漏选都不得分) 1、已知一段文本有1382个字符,使用了1382个字节进行存储,这段文本全部是由a、b、c、d、e这5个字符组成,a出现了354次,b出现了483次,c出现了227次,d出现了96次,e出现了232次,对这5个字符使用哈夫曼(Huffman)算法进行编码,则以下哪些说法正确() A、使用哈夫曼算法编码后,用编码值来存储这段文本将花费最少的存储空间 B、使用哈夫曼算法进行编码,a、b、c、d、e这5个字符对应的编码值是唯一确定的 C、使用哈夫曼算法进行编码,a、b、c、d、e这5个字符对应的编码值可以有多套,但每个字符编码的位(bit)数是确定的 D、b这个字符的哈夫曼编码值位数应该最短,d这个字符的哈夫曼编码值位数应该最长 2、下列表达式中,不合法的是() 已知:double d = 3.2; int n = 3; A、d<<2; B、d/n C、!d && (n-3) D、(d-0.2)|n 3、下面描述正确的是() A、while循环语句的循环体至少执行1次 B、do-while循环可以写成while循环的格式 C、continue语句可以出现在各种循环体中 D、break语句不可以出现在循环体内 4、关于内联函数正确的是() A、类的私有成员函数不能作为内联函数 B、在所有类说明中内部定义的成员函数都是内联函数 C、类的保护成员函数不能作为内联函数 D、使用内联函数的地方会在运行阶段用内联函数体替换掉 5、下面模板声明中,哪些是非法的() A、template<class Type>class C1; B、template<class T,U , class V>class C2; C、template<class C1 , typename C2>class C3{}; D、template<typename myT , class myT>class C4{}; 6、在使用浏览器打开一个网页的过程中,浏览器会使用的网络协议包括() A、DNS B、TCP C、HTTP D、Telnet 7、下面属于构造散列函数的方法是() A、直接定址法 B、数字分析法 C、乘余取整法 D、平方取中法 8、拷贝构造函数的特点是() A、该函数名同类名,也是一种构造函数,该函数返回自身引用 B、该函数只有一个参数,必须是对某个对象的引用 C、每个类都必须有一个拷贝初始化构造函数,如果类中没有说明拷贝构造函数,则编译器系统会自动生成一个缺省拷贝构造函数,作为该类的保护成员 D、拷贝初始化构造函数的作用是将一个已知对象的数据成员值拷贝给正在创建的另一个同类的对象 9、下列关于虚函数的说法正确的是() A、在构造函数中调用类自己的虚函数,虚函数的动态绑定机制还会生效。 B、在析构函数中调用类自己的虚函数,虚函数的动态绑定机制还会生效。 C、静态函数不可以是虚函数 因为静态成员函数没有this,也就没有存放vptr的地方,同时其函数的指针存放也不同于一般的成员函数,其无法成为一个对象的虚函数的指针以实现由此带来的动态机制。静态是编译时期就必须确定的,虚函数是运行时期确定的。 D、虚函数可以声明为inline inline函数和virtual函数有着本质的区别,inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数体现的是一种运行期机制。 因此,内联函数是个静态行为,而虚函数是个动态行为,他们之间是有矛盾的。 函数的inline属性是在编译时确定的,然而,virtual的性质则是在运行时确定的,这两个不能同时存在,只能有一个选择,文件中声明inline关键字只是对编译器的建议,编译器是否采纳是编译器的事情。 我并不否认虚函数也同样可以用inline来修饰,但你必须使用对象来调用,因为对象是没有所谓多态的,多态只面向行为或者方法,但是C++编译器,无法保证一个内联的虚函数只会被对象调用,所以一般来说,编译器将会忽略掉所有的虚函数的内联属性。 相关知识点:什么函数不能声明为虚函数? 一个类中将所有的成员函数都尽可能地设置为虚函数总是有益的。 设置虚函数须注意: 1:只有类的成员函数才能说明为虚函数; 2:静态成员函数不能是虚函数; 3:内联函数不能为虚函数; 4:构造函数不能是虚函数; 5:析构函数可以是虚函数,而且通常声明为虚函数。 10、下列对函数double add(int a , int b)进行重载,正确的是() A、int add(int a ,int b ,int c) B、int add(double a , double b) C、double add(double a , double b) D、int add(int a , int b) 三、填空题(15空,每空2分) 1、以下代码是用来计算100以内的素数的个数,请把相应的空填上。 [cpp] view plaincopy
1. struct prime_number_node
2. {
3. int prime_number;
4. prime_number_node* next;
5. };
6.
7. int calc_prime_number()
8. {
9. prime_number_node* list_head = new prime_number_node();
10. list_head->next = NULL;
11. list_head->prime_number = 2;
12. prime_number_node* list_tail = list_head;
13. for(int number = 3 ; number < 100 ; number++)
14. {
15. int remainder;
16. prime_number_node* cur_node_ptr = list_head;
17. while(cur_node_ptr != NULL)
18. {
19. remainder = number%cur_node_ptr->prime_number;
20. if(remainder == 0)
21. {
22. //1
23. }
24. else
25. {
26. //2
27. }
28. }
29. if(remainder != 0)
30. {
31. prime_number_node* new_node_ptr = new prime_number_node();
32. new_node_ptr->prime_number = number;
33. new_node_ptr->next = NULL;
34. list_tail->next = new_node_ptr;
35. //3
36. }
37. }
38. int result = 0;
39. while(list_head != NULL)
40. {
41. result++;
42. prime_number_node* temp_ptr = list_head;
43. list_head = list_head->next;
44. //4
45. }
46. return result;
47. }
1、2、3、4四行代码依次为:break; cur_node_ptr = cur_node_ptr->next; list_tail = list_tail->next; delete temp_ptr; 2、已知集合A和B的元素分别用不含头结点的单链表存储,函数difference()用于求解集合A与B的差集,并将结果保存在集合A的单链表中。例如,若集合A={5,10,20,15,25,30},集合B={5,15,35,25},完成计算后A={10,20,30}。 链表结点的结构类型定义如下: [cpp] view plaincopy
1. struct node
2. {
3. int elem;
4. node* next;
5. };
6.
7. void difference(node** LA , node* LB)
8. {
9. node *pa , *pb , *pre , *q;
10. pre = NULL;
11. //1
12. while(pa)
13. {
14. pb = LB;
15. while( ) //2
16. pb = pb->next;
17. if( ) //3
18. {
19. if(!pre)
20. *LA = ; //4
21. else
22. = pa->next; //5
23. q = pa;
24. pa = pa->next;
25. free(q);
26. }
27. else
28. {
29. ; //6
30. pa = pa->next;
31. }
32. }
33. }
1、2、3、4、5、6六行代码依次为 pa = *LA; pb && pa->elem != pb->elem pb pa->next pre->next pre = pa; 代码中的指针pa用于指向集合A的元素;pb指向集合B的元素;临时指针q指向需要被删除的元素;pre用于实现删除时结点的链接,与pa保持所指结点的前后继关系。 |