华为 2017 秋季校招笔试题(C++ 后台)

1.删除字符串中的指定字符

1.1 问题描述

输入两个字符串M和N,从字符串M中删除字符串N中所有的字符。例如,输入”abcda”和”ac”,则删除之后的第一个字符串变成”bd”。

1.2 问题求解

这个比较简单,给出如下参考代码:

#include <string>
using namespace std;

void deleteCharacter(string& str0,string& str1){
	for(int i=0;i<str0.length();){
		if(str1.find(str0[i])!=string::npos){
			str0.erase(i,1);
			continue;
		}
		++i;
	}
}

2.成绩排名

2.1 问题描述

题目总共包含如下两种格式的字符串命令:
LOD GRADE命令,其格式:
LOD GRADE:NAME=XiaoMing,MATH=80,LANG=90;
(1) 此命令用于导入学生成绩
(2) NAME字段表示学生姓名
(3) MATH字段表示学生数学成绩
(4) LANG字段表示语文成绩
(5) MATH字段和LANG字段顺序不一定MATH在前,LANG在后
(6) 相同的分数,名次相同,后面的名次空缺;例如100,99,99,99,98,98,名次:1,2,2,2,5,5
(7) 此命令会连续执行,直到遇到第一个LST GRADE

LST GRADE命令,其格式:
LST GRADE:NAME=XiaoMing;
(1) 此命令用于查询学生成绩
(2) NAME字段表示学生姓名
(3) 查询结果格式:姓名 数学 语文 总分 数学排名 语文排名 总排名
(4) 每组用例,此命令仅执行一次

输入: 连续多组LOD GRADE后跟一个LST GRADE查询命令
输出: 输出查询格式为:
姓名 数学 语文 总分 数学排名 语文排名 总排名
样例输入: LOD GRADE:NAME=XiaoMing,MATH=80,LANG=90;
LOD GRADE:NAME=XiaoHong,LANG=60,MATH=100;
LOD GRADE:NAME=XiaoMei,MATH=70,LANG=90;
LST GRADE:NAME=XiaoHong;
样例输出: XiaoHong 100 60 160 1 3 2

2.2 问题求解

此问题也不难,没有涉及到复杂的算法,就是比较繁琐,主要考察数据的表示,字符串的提取与排序,下面给出参考代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

struct Student {
	string name;
	int math;
	int lang;
	Student(){
		this->name="";
		this->math=0;
		this->lang=0;
	}
	Student(string name,int math,int lang){
		this->name=name;
		this->math=math;
		this->lang=lang;
	}
	bool operator==(const Student& ele){
		return this->name==ele.name;
    }
};

//自定义比较函数,数学排名
bool compareMath(const Student& left,const Student& right){
	return left.math>right.math; //降序排列
}

//自定义比较函数,语文排名
bool compareLang(const Student& left,const Student& right){
	return left.lang>right.lang; //降序排列
}

//自定义比较函数,总分排名
bool compareTotal(const Student& left,const Student& right){
	return left.math+left.lang>right.math+right.lang; //降序排列
}

int main(){
	vector<Student> studentVec;
	string input;
	Student student;
	vector<string> splitedRes;
	while(getline(cin,input)){
		int s=input.find("NAME=");
		int e=input.find(',',s);
		if(input.find("LOD GRADE")!=string::npos){  //输入成绩
			student.name=input.substr(s+5,e-s-5);

			s=input.find("MATH=");
			e=input.find(',',s);
			if(e==string::npos) e=input.length()-1;
			student.math=stoi(input.substr(s+5,e-s-5));
			
			s=input.find("LANG=");
			e=input.find(',',s);
			if(e==string::npos) e=input.length()-1;
			student.lang=stoi(input.substr(s+5,e-s-5));
			studentVec.push_back(student);
		}else {                                     //查询成绩
			e=input.length()-1;
			string name=input.substr(s+5,e-s-5);
			Student student;
			//数学排名
			std::sort(studentVec.begin(),studentVec.end(),compareMath);
			vector<Student>::iterator it=find(studentVec.begin(),studentVec.end(),Student(name,0,0));
			student=*it;
			while(it!=studentVec.begin()&&(it-1)->math==it->math) --it;
			int mathRank=it-studentVec.begin()+1;

			//语文排名
			std::sort(studentVec.begin(),studentVec.end(),compareLang);
			it=find(studentVec.begin(),studentVec.end(),Student(name,0,0));
			while(it!=studentVec.begin()&&(it-1)->lang==it->lang) --it;
			int langRank=it-studentVec.begin()+1;

			//总分排名
			std::sort(studentVec.begin(),studentVec.end(),compareTotal);
			it=find(studentVec.begin(),studentVec.end(),Student(name,0,0));
			while(it!=studentVec.begin()&&(it-1)->math+(it-1)->lang==it->math+it->lang) --it;
			int totalRank=it-studentVec.begin()+1;
			cout<<student.name<<" "<<student.math<<" "<<student.lang<<" "<<student.math+student.lang<<" "<<mathRank<<" "<<langRank<<" "<<totalRank<<endl;
			studentVec.clear();
		}
	}
	getchar();
}

3.字符串变换最小费用

3.1 问题描述

给出两个字串A,B。将A字串转化为B字串,转化一共有两种方式:删除连续的n个字符,一次操作费用为2。增加连续的n个字符(增加的字符是什么由你决定),一次操作费用为n+2。求把A变为B最小费用。

输入:
第一行输入一个正整数T(1 <= T <= 10),表示有T组测试数据。
对于每组测试数据,有两行字符串A, B(字符串长度不超过2000,字符仅包含小写字母)。

输出:
对于每组测试数据,输出一行一个整数,表示最小费用。

样例输入:

2
dsafsadfadf
fdfd
aaaaaaaa
bbbbbbbb

样例输出:

7
12

答案提示:
“dsafsadfadf” 变成 “fdfd” 最少的代价的一种方法是:
(1)“dsafsadfadf” -> “f” 删除连续的10个,代价2 ;
(2)“f” -> “fdfd” 增加连续的3个(”dfd”),代价为3 + 2 = 5
总共的最小代价为2 + 5 = 7,其他方法的代价都不小于7 。
“aaaaaaaa” 变成 “bbbbbbbb” 最小代价的一种方法是:
(1)“aaaaaaaa” 全部删除,代价2;
(2)增加8个连续的’b’,代价10 。
总共的最小代价为2 + 10 = 12 。
注意,有些最优的方案可能要做多次的删除和增加操作,不限于两次。

3.2 递归法求解

问题分析:
从给定的问题描述,我们可以得到如下几条信息:
(1)A串变为B串,只有两种变换的方式,一是删除,二是增加。增加和删除的位置可以在A串中的任意位置;
(2)每一次删除和增加都需要额外的代价,因此,对同一段字符,应该使用贪心思想,尽可能的连续删除和连续增加;
(3)A串和B串的相同的首尾子串是不需要考虑的。

除去相同的首尾子串,得到的子串A’和B’,将A’变为B’时,因为此时的A’的首尾字符与B’的首尾字符是不相同的,所以,对A’此时的操作有两种:
(1)对A’从左起和右起使用贪心的思想删除连续的字符;
(2)对A’从左起和右起用贪心的思想分别增加B’的左起连续的字符和B’的右起连续的字符。
这里为什么不考虑从A的中间部分开始插入和删除,是因为这样做的话,A’的首尾位字符与B’的首尾字符还是不相同,还是需要进行删除或者增加的操作,很明显这样不是最优的,所以抛弃这种做法。

具体实现请,参考如下代码:

/*******************************************
*@brief:将字符串a变为字符串b的最小费用
*@param:a:字符串a;b:字符串b;i:a的起始下标;ir:a的结束下标;j:b的起始下标;jr:b的结束下标
*******************************************/
int func1 (const char a[] ,const char b[] ,int i ,int ir ,int j ,int jr) {
    //ab有相同前缀,则去除掉也不影响
    while (i <= ir && j <= jr && a[i] == b[j]) {
        i++ ;
        j++ ;
    }
    // ab有相同后缀,则去除掉也不影响
    while (i <= ir && j <= jr && a[ir] == b[jr]) {
        ir-- ;
        jr-- ;
    }
    if (i > ir) {
        //如果a是空串,则只需增加操作,代价是b的长度+2
        return (jr + 1 - j) + 2 ;
    } else if (j > jr) {
        //如果b是空串,则只需删除操作,代价是2
        return 2 ;
    }
    //如果ab无公共前后缀,则代价是求a的所有前后子串转为b的最小值
    //最坏的情况是将a全部删除再增加到b
    int tmp = 2 + (jr + 1 - j) + 2 ;
    //a的非空后子串
    for (int k = i + 1 ; k <= ir ; k++)
        tmp = min (tmp ,2 + func1 (a ,b ,k ,ir ,j ,jr)) ;
    //a的非空前子串
    for (int k = ir - 1 ; k >= i ; k--)
        tmp = min (tmp ,2 + func1 (a ,b ,i ,k ,j ,jr)) ;
	//在a的左边增加连续的b的非空前子串
	for (int k = j + 1 ; k <= jr ; k++)
		tmp = min (tmp ,(k - j) + 2 + func1 (a ,b ,i ,ir ,k ,jr)) ;
	//在a的右边增加连续的b的非空后子串
	for (int k = jr - 1 ; k >= j ; k--)
		tmp = min (tmp ,(jr - k) + 2 + func1 (a ,b ,i ,ir ,j ,k)) ;
    return tmp ;
}

运行结果:

cout<<func1("1aaa1","aaa",0,4,0,2)<<endl;  //输出4
cout<<func1("aaa","1aaa1",0,2,0,4)<<endl;  //输出6

3.2 动态规划法求解

递归法易于理解,但是存在对子问题的重复计算,时间效率低下,可以将子问题的结果存储起来,把递归实现,转换为迭代实现,这样就变成了动态规划。

递归法是自顶向下,而动态规划是自底向上递归法是需要某个结果时就调用自己来计算,动态规划把每次递推的结果保存在数组中。因为这里有i,ir,j,jr一个4个变量,所以其实需要一个4维数组,这里用了一个宏代替,将4维数组通过下标转变变为一维数组。

具体实现参考如下代码:

int func2 (const string &a, const string &b)  {
    const int la = (int) a.length () ;
    const int lb = (int) b.length () ;
    vector<int> ret (la * la * lb * lb) ;
	#define VRET(a ,b ,c ,d) (ret[(a) * la * lb * lb + (b) * lb * lb + (c) * lb + (d)])
    for (int ix = la - 1 ; ix >= 0 ; ix--)
        for (int irx =ix ; irx < la ; irx++)
            for (int jx = lb - 1 ; jx >= 0 ; jx--)
                for (int jrx =jx ; jrx < lb ; jrx++) {
                    int i = ix ;
                    int ir = irx ;
                    int j = jx ;
                    int jr = jrx ;
                    while (i <= ir && j <= jr && a[i] == b[j]) {
                        i++ ;
                        j++ ;
                    }
                    while (i <= ir && j <= jr && a[ir] == b[jr]) {
                        ir-- ;
                        jr-- ;
                    }
                    if (i > ir) {			//A为空串
                        VRET (ix ,irx ,jx ,jrx) = (jr + 1 - j) + 2 ;
                        continue ;
                    } else if (j > jr) {    //B为空串
                        VRET (ix ,irx ,jx ,jrx) = 2 ;
                        continue ;
                    }
                    int tmp = 2 + (jr + 1 - j) + 2 ;   //最坏情况,将A全部删除再增加到B
                    for (int k = i + 1 ; k <= ir ; k++) 
                        tmp = min (tmp ,2 + VRET (k ,ir ,j ,jr)) ;
                    for (int k = ir - 1 ; k >= i ; k--)
                        tmp = min (tmp ,2 + VRET (i ,k ,j ,jr)) ;
					for (int k = j + 1 ; k <= jr ; k++)
						tmp = min (tmp ,(k - j) + 2 + VRET (i ,ir ,k ,jr));
					for (int k = jr - 1 ; k >= j ; k--)
						tmp = min (tmp ,(jr-k) + 2 + VRET (i ,ir ,j ,k));

                    VRET (ix ,irx ,jx ,jrx) = tmp ;
                    continue ;
                }
    return VRET (0 ,la - 1 ,0 ,lb - 1) ;
	#undef VRET
}

运行结果:

cout<<func2("dsafsadfadf","fdfd")<<endl;   //7
cout<<func2("aaaaaaaa","bbbbbbbb")<<endl;  //12

参考文献

CSDN论坛

1.static有什么用途?(请至少说明两种) 1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用 2.引用与指针有什么区别? 1) 引用必须被初始化,指针不必。 2) 引用初始化以后不能被改变,指针可以改变所指的对象。 3) 不存在指向空值的引用,但是存在指向空值的指针。 3.描述实时系统的基本特性 在特定时间内完成特定的任务,实时性与可靠性。 4.全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 全局变量储存在静态数据库,局部变量在堆栈。 5.什么是平衡二叉树? 左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 6.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。 7.什么函数不能声明为虚函数? constructor函数不能声明为虚函数。 8.冒泡排序算法的时间复杂度是什么? 时间复杂度是O(n^2)。 9.写出float x 与“零值”比较的if语句。 if(x>0.000001&&x<-0.000001) 10.Internet采用哪种网络协议?该协议的主要层次结构? Tcp/Ip协议 主要层次结构为: 应用层/传输层/网络层/数据链路层/物理层。 11.Internet物理地址和IP地址转换采用什么协议? ARP (Address Resolution Protocol)(地址解析協議) 12.IP地址的编码分为哪俩部分? IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。 13.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。 循环链表,用取余操作做 14.不能做switch()的参数类型是: switch的参数不能为实型。 1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分) int a = 4; (A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++); a = ? 答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a; 改后答案依次为9,10,10,11 2.某32位系统下, C++程序,请计算sizeof 的值(5分). char str[] = “http://www.ibegroup.com/” char *p = str ; int n = 10; 请计算 sizeof (str ) = ?(1) sizeof ( p ) = ?(2) sizeof ( n ) = ?(3) void Foo ( char str[100]){ 请计算 sizeof( str ) = ?(4) } void *p = malloc( 100 ); 请计算 sizeof ( p ) = ?(5) 答:(1)17 (2)4 (3) 4 (4)4 (5)4 3. 回答下面的问题. (4分) (1).头文件中的 ifndef/define/endif 干什么用?预处理 答:防止头文件被重复引用 (2). #i nclude 和 #i nclude “filename.h” 有什么区别? 答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。 (3).在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明? 答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变 量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调 用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。 (4). switch()中不允许的数据类型是? 答:实型 4. 回答下面的问题(6分) (1).Void GetMemory(char **p, int num){ *p = (char *)malloc(num); } void Test(void){ char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); } 请问运行Test 函数会有什么样的结果? 答:输出“hello” (2). void Test(void){ char *str = (char *) malloc(100); strcpy(str, “hello”); free(str); if(str != NULL){ strcpy(str, “world”); printf(str); } } 请问运行Test 函数会有什么样的结果? 答:输出“world” (3). char *GetMemory(void){ char p[] = "hello world"; return p; } void Test(void){ char *str = NULL; str = GetMemory(); printf(str); } 请问运行Test 函数会有什么样的结果? 答:无效的指针,输出不确定 5. 编写strcat函数(6分) 已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc); 其中strDest 是目的字符串,strSrc 是源字符串。 (1)不调用C++/C 的字符串库函数,请编写函数 strcat 答: VC源码: char * __cdecl strcat (char * dst, const char * src) { char * cp = dst; while( *cp ) cp++; /* find end of dst */ while( *cp++ = *src++ ) ; /* Copy src to end of dst */ return( dst ); /* return dst */ } (2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值? 答:方便赋值给其他变量 6.MFC中CString是类型安全类么? 答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换 7.C++中为什么用模板类。 答:(1)可用来创建动态增长和减小的数据结构 (2)它是类型无关的,因此具有很高的可复用性。 (3)它在编译时而不是运行时检查数据类型,保证了类型安全 (4)它是平台无关的,可移植性 (5)可用于基本数据类型 8.CSingleLock是干什么的。 答:同步多个线程对一个数据类的同时访问 9.NEWTEXTMETRIC 是什么。 答:物理字体结构,用来设置字体的高宽大小 10.程序什么时候应该使用线程,什么时候单线程效率高。 答:1.耗时的操作使用线程,提高应用程序响应 2.并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。 3.多CPU系统中,使用线程提高CPU利用率 4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独 立的运行部分,这样的程序会利于理解和修改。 其他情况都使用单线程。 11.Windows是内核级线程么。 答:见下一题 12.Linux有内核级线程么。 答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两 种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序 中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度 和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一 种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部 需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支 ,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线 程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不 到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占 用了更多的系统开支。 Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程 13.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中? 答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理 堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上 14.使用线程是如何防止出现大的波峰。 答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提 高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队 等候。 15函数模板与类模板有什么区别? 答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化 必须由程序员在程序中显式地指定。 16一般数据库若出现日志满了,会出现什么情况,是否还能使用? 答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记 录日志。也就是说基本上处于不能使用的状态。 17 SQL Server是否支持行级锁,有什么好处? 答:支持,设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据 的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不 被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的迸发性。 18如果数据库满了会出现什么情况,是否还能使用? 答:见16 19 关于内存对齐的问题以及sizof()的输出 答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能 地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问 ;然而,对齐的内存访问仅需要一次访问。 20 int i=10, j=10, k=3; k*=i+j; k最后的值是? 答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低 21.对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现? 答:将操作多个表的操作放入到事务中进行处理 22.TCP/IP 建立连接的过程?(3-way shake) 答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。   第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状 态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个 SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;   第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1) ,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 23.ICMP是什么协议,处于哪一层? 答:Internet控制报文协议,处于网络层(IP层) 24.触发器怎么工作的? 答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT 、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数 据的处理必须符合由这些SQL 语句所定义的规则。 25.winsock建立连接的主要实现步骤? 答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept() 等待客户端连接。 客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv( ),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。 服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连 接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock et()关闭套接字。 26.动态连接库的两种方式? 答:调用一个DLL中的函数有两种方法: 1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数 ,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向 系统提供了载入DLL时所需的信息及DLL函数定位。 2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa dLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的 出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了 。 27.IP组播有那些好处? 答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧 消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包 到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无 论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播 技术的核心就是针对如何节约网络资源的前提下保证服务质量。
1. 虚函数是可以[New一个对象的时候要根据虚函数的函数体来填虚表;而内联函数没有函数体,只是在预编译阶段展开]内联的,这样就可以减少函数调用的开销,提高效率(错误) 2. 一个类里可以同时存在[同一个类里无论什么函数都不能函数名和参数完全一样]参数和函数名都相同的虚函数与静态函数(错误) 3. 父类的析构函数是非虚的,但是子类的析构函数是虚的,delete子类指针(指向该子类对象)[特殊情况,参见题5],会调用父类的析构函数(正确)//任何情况下删除子类都会调用到父类的析构函数 4.对于下面的类CA,sizeof(CA) = _B_: A. 4 B. 8 C. 12 D. 16 class CA { public: CA(); virtual ~CA(); //因为有虚函数,所以会有4个字节的虚表指针 private: int m_iTime; //成员变量4个字节 public: int GetTime(); int SetTime(int iTime); }; 5.下面这段程序,打印结果是_A_: A. 1 B. 2 C. 3 D. 以上都不对 int g_iCount = 0; class CParent { public: CParent() {} ~CParent() {g_iCount += 1;} }; class CSon : public CParent { public: CSon() {} ~CSon() {g_iCount += 2;} }; main() { CParent* p = new CSon(); delete p[由于p被声明成父类指针,并且父类和子类的析构函数都非虚,因此delete操作只能根据p指针声明的类型来调用父类的析构函数]; std::cout << g_iCount << std::endl; } 6.请问下面这段程序的输出结果是_A_: A. 2,1, B. 2,2, C. 1,1, D. 1,2, class CParent { public: CParent() {} virtual ~CParent() {} public: virtual void Print() { std::cout << "1,"; }; }; class CSon : public CParent { public: CSon() {}; virtual ~CSon() {}; public: void Print() { std::cout << "2,"; }; }; void Test1(CParent& oParent[这里是引用了一个外部对象,该对象的虚表不会发生变化]) {oParent.Print();} void Test2(CParent oParent[这里会在栈空间内重新构造一个CParent类的对象,如果传入实参的类型与CParent不同则虚表会发生变化]) {oParent.Print();} main() { CSon * p = new CSon(); Test1(*p); //这里只是一个引用 Test2(*p); //这里会在栈空间重新构造Cparent类对象 delete p; } 7.请问下面这段程序的输出结果是_D_: A. 2,1, B. 2,2, C. 1,1, D. 1,2, class CParent { public: CParent() {} virtual ~CParent() {} public: void Print(){ std::cout << "1," ; }; }; class CSon : public CParent { public: CSon() {} virtual ~CSon() {} public: void Print(){ std::cout << "2,"; }; }; main() { CSon oSon; CParent * pParent = &oSon; CSon * pSon = &oSon; pParent->Print(); pSon->Print();[由于父类和子类的Print函数都非虚,所以根据指针类型决定调用关系] } 8.请问下面这段程序的输出结果是_C_: A. 2,1, B. 2,2, C. 1,2, D. 1,1, class CParent { public: CParent() {Print();} virtual ~CParent() {} public: virtual void Print(){ std::cout << "1,"; } }; class CSon : public CParent { public: CSon() {Print();} virtual ~CSon() {} public: void Print(){ std::cout << "2,"; } }; main() { CParent * pParent = new CSon()[类的构造过程遵循压栈原则,构造过程中虚表尚未建立成功,是静态调用虚函数]; delete pParent; } 9.请问下面这段程序的输出结果是_D_: A. 2,2, B. 2, C. 输出结果不确定 D. 以上都不对 class CParent { public: CParent() {Print();[构造子类对象时调用到父类的构造函数,但父类的Print函数是纯虚的,没有实现,所以这里的调用不成功,编译会出错]} virtual ~CParent() {} public: virtual void Print() = 0; }; class CSon : public CParent { public: CSon() {Print();} virtual ~CSon() {} public: void Print() { std::cout << "2,"; }; }; main() { CParent * pParent = new CSon(); delete pParent; } 10.请仔细阅读以下程序: class Base { public: virtual bool operator == (int iValue) { std::cout << "I am Base class !" << std::endl; return true; } virtual ~Base(){} }; class Derive: public Base { public: virtual bool operator == (int iValue) { std::cout << "I am Derive class !" << std::endl; return true; } virtual ~Derive(){} }; int main() { Derive derive; Base* pBase = &derive; Derive* pDerive = &derive; *pBase == 0; *pDerive == 0;[重载操作符声明为virtual使操作符产生多态性] return 0; } 程序的输出结果是_B_: A、I am Base class ! I am base class ! B、I am Derive class ! I am Derive class ! C、I am base class ! I am Derive class ! D、I am Derive class ! I am Base class !
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值