1. 分析下列程序
(1)
#include<iostream>
#include<string.h>
using namespace std;
int main() {
int*(*a)[3][4]={0};
cout<<sizeof(*a);
}
(2)
#include<iostream>
#include<string.h>
using namespace std;
int main() {
int*a[3][4]={0};
cout<<sizeof(a);
}
(3)
#include<iostream>
#include<string.h>
using namespace std;
int main() {
inta[3][4]={0};
cout<<sizeof(a);
}
2. 题意为输入设定全部是大写(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]。
2. 分析下列程序:
#include<iostream>
#include<string.h>
using namespace std;
class CParent {
public:virtualvoid Intro() {
cout<<"I'mParent"<<endl;
Hobby();
}
virtualvoid Hobby() {
cout<<"Ilike football!";
}
};
class CChild:public CParent{
public:virtualvoid Intro() {
cout<<"I'mChild"<<endl;
Hobby();
}
virtualvoid Hobby() {
cout<<"I like basketball!";
}
};
int main() {
CChild*pChild=new CChild();
CParent*pParent=(CParent *)pChild;
pParent->Intro();
return0;
}
一个类如果定义了虚函数,则不管是类中还是类外,对这个函数的调用都是通过对象的虚函数表,所以Intro(){hobby()};相当于vptr[0](this*){this->vptr[1](this)},虚函数表的第一二项分别是Intro()和hobby()的地址,而且子类已经把这两个都重写了,所以调用的自然都是子类的函数。
CParent *pParent = (CParent *)pChild;
这个是上行转换,即把子类的引用或指针转换为基类表示。这个是安全的。
所以上面的语句的作用是把pChild指针转换为基类指针并赋值给pParent,但是它指向的内容是没变的,所以最终结果是pChild指针和pParent指针都指向了child类。
而且由于基类的方法都已经声明为了虚函数,所以最后pParent->Intro();根据虚函数表就是调用了child类里面的方法。
每一个虚函数对象对应一个虚表指针;
类和虚表对应,对象和虚表指针对应;
不管怎么转换,对象没变,对象对应的虚表指针就不变;
虚函数的作用是可以通过基类指针或引用 访问基类和派生类中的虚函数,从而实现动态关联。如此,基指针指向哪个类的对象就调用哪个类的函数。如果不加virtual,就变成了静态关联.
3. 调用Play函数需要将5隐式类型转换为Play函数中的形参b,会调用B的B(int i): data(i),打印“constructed by parameter5”。Play函数返回时需要调用B的复制构造函数给对象temp初始化。Play函数返回后需要调用b的析构函数,将Play函数的形参释放,打印“destructed”。 main函数返回后需要释放temp,打印“destructed”。
4. 结构体在完成定义之前是incompletetype(不完全类型),不完全类型不能定义对象,只能定义引用和指针,或者用于声明函数的形参和返回值类型。
struct成员类型不可以是它自己。因为会递归定义。理论上这样导致结构体的大小不能被计算(无限大小)。所以不能在结构体里的成员类型是结构体本身。但是成员可以定义为该结构体的指针。就像你上面这段代码。因为指针的大小是已知的(随编译器和操作系统而定)。所以可以定义为该结构体的指针,但不是该结构体。
5. 分析下列程序:
#include<iostream>
#include<string.h>
using namespace std;
int main(void) {
http://www.taobao.com
cout<<"hello!"<<endl;
return0;
}
分析:http在此处相当于label,意思是此处可以用go to heep;来跳转到http这个label标注的语句。
双斜杠之后的www.csdn.net被当做注释了,那么前面的http:是否合法?这就是C++中一个几乎不会被用到的语法,标签。
带标签的语句是一种特殊的语句,在语句前面有一个标识符(即标签,上段代码中的http)和一个冒号。使用gotolabel就可以跳到标签处执行,比如可以在代码中写goto http,这样就会执行cout语句了。
case就是一种标签,case关键字和它对应的值一起,称为case标签。
类中的public、private、protect也是标签,称为成员访问标签。
6. 分析下列程序:没有输出结果,程序运行出错。
#include<iostream>
#include<string.h>
using namespace std;
class parent {
public:
virtual voidoutput();
};
void parent::output() {
printf("parent!");
}
class son:public parent {
public:
virtual voidoutput();
};
void son::output() {
printf("son!");
}
int main(void) {
son s;
memset(&s,0,sizeof(s));
parent& p=s;
p.output();
return 0;
}
分析一:void *memset(void *s,int ch, size_t n);函数解释:将s中前n个字节(typedef unsigned int size_t)用ch替换并返回s。作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法,通常为新申请的内存做初始化工作.
memset将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,
本代码中, 不虚函数链表地址也清空了, 所以p.output调用失败。 output函数的地址编程0
分析二:按道理来说,应该输出“son!”,但是因为对引用的指针地址进行申请内存,会清空引用内所有指针函数。所以程序运行出错,没有输出结果。
7.类中重载运算符的一般格式是:
类名operator运算符(参数)
声明类的运算符成员函数时可以省略一个参数例如,一个参数时可以不用形参,参数只用一个形参,因为成员函数有this指针表示自己。但是若是类的友元函数声明运算符时,不能省略,因为这种情况下this指针并不是该类的类型。
8.分析下列程序
#include<iostream>
#include<string.h>
using namespace std;
class Test{
public:
/* 空格填写*/ int a;
/* 空格填写*/ int b;
public:
Test::Test(int_a,int _b):a(_a) {
b=_b;
}
};
int Test::b;
int _tmain(void) {
Testt1(0,0),t2(1,1);
t1.b=10;
t2.b=10;
printf("%u %u%u %u",t1.a,t1.b,t2.a,t2.b);
return 0;
}
A const const/static
B -/static
C const static/static D None
分析:BC对于成员变量a,若它为const类型,那么必须要使用Test::Test(int_a ,int _b):a(_a)这种初始化形式,若它为普通成员变量,也可以采取Test::Test(int _a,int_b):a(_a)这种形式,所以a可以为const或者普通类型,由于b没有采取Test::Test(int_a,int_b):b(_b)这种形式,所以b一定不是const类型,有main()中的t1.b和t2.b的输出都是20可以知道,b是静态变量。
补充:const static数据成员可以在类内初始化,也可以在类外,不能在构造函数中初始化,也不能在构造函数的初始化列表中初始化。
static数据成员只能在类外,即类的实现文件中初始化,也不能在构造函数中初始化,不能在构造函数的初始化列表中初始化;
const数据成员只能在构造函数的初始化列表中初始化;
普通数据成员不能在类内初始化,可以在构造函数中初始化,也可以在构造函数的初始化列表中初始化;
9.分析下列程序:
#include<iostream>
#include<string.h>
using namespace std;
int main() {
char *sp,s[10];
sp="Hello";
cout<<sp;
return 0;
}
分析:有warning,deprecated conversion from string constant to ‘char’.但是能编译运行成功。
字符数组的初始化以及指向字符串的指针, B选项不能直接将字符串赋值给数组名,C选项不能将两个字符数组直接互相赋值,D选项中不能这样定义 char mark[],必须指定长度或者直接初始化。
10.extern声明多文件共享变量的方法总结一下:
1).在一个源文件中定义,在其他需要使用的源文件中用extern声明。(仅一处定义,多处extern)
2).在一个源文件中定义,在其对应的头文件中extern声明,在其他需要使用该共享变量的源文件中包含该头文件即可。(更加标准的做法)
11.C++面向对象编程语言中,以下不正确的是(AD)
A接口中可以用虚方法
B 一个类可以实现多个接口
C 接口不能被实例化
D接口中可以包含已经实现的方法
分析:这道题正确答案AD,首先所谓的接口是指只包含纯虚函数的抽象类,和普通的抽象类含不一样。所以A不对,必须是纯虚函数。然后B是正确的没有问题。然后是C,刚才说接口是特殊的抽象类,抽象类的唯一左右就是创建派生类,不能定义抽象类的对象,所以C是正确的。对于D,接口即只包含纯虚函数的抽象类,所以D是不对的。
12. A选项:在组合类的析构函数中并不需要显式调用其成员对象的析构函数,因为当执行组合类的析构函数时,其数据成员也会被销毁,对于类成员对象来说,成员对象销毁时,程序会自动调用其析构函数;不过对于组合类中new的指向类成员的指针,组合类需要自己去delete该指针;
B选项:显然是错的,在类继承层次中,基类在上,派生类在下,所以可以自动进行向上类型转换,即可以使基类指针和引用指向派生类对象,而不可以使派生类指针和引用指向基类对象;
C选项:对的,构造函数可以根据参数的不同实现函数重载,而因为析构函数没有参数,对于一个类来说也是唯一的,所以是不能重载的;
D选项:即派生类想要重写继承来的成员函数,需要用到virtual函数,来实现动态多态。
13. sizeof(arr)求出了为arr的长度乘以arr类型所占的字节数,这里arr是char型的,每个数据占一个字节,所sizeof(arr)得到8;
sizeof(str)这里str是字符指针,32为系统中指针占4个字节,所以为4;strlen(str)求字符串长度,这里的字符串由字符数组得到,字符数组中赋值的是字符的ASCII值;
其中ASCII值为0的代表字符中的‘\0’也就是字符串结束的标志,所得到的长度为5;