C++复习资料.docx
《C++复习资料.docx》由会员分享,可在线阅读,更多相关《C++复习资料.docx(26页珍藏版)》请在冰点文库上搜索。
C++复习资料
一、问答题(每小题分,共分)
1.C++的类库主要有哪2种?
答:
主要有Borland的OWL(ObjectWindowsLibrary)和Microsoft的MFC。
1.简单叙述类的确定与划分的基本方法
答:
将目标任务中具有共性的成分确定为一个类;可以按照以下步骤进行分析:
第一,要判断该事物是否有一个以上的实例;第二,要判断类的实例中有没有绝对的不同点,如果没有,则它同样可能被确定为一个类。
2.什么是抽象类?
抽象类能否用来创建对象?
抽象类的作用是什么?
答:
含有纯虚函数的类是抽象类。
抽象类不能声明对象实例。
只能声明指针或作为基类派生子类。
答案见课本226页
3.什么是虚函数?
什么是纯虚函数?
什么是多态类?
虚函数是保留字virtual定义的成员函数,基类的虚函数在派生类仍是虚函数。
纯虚函数是在类中声明但没有定义的虚函数
4.试论述类的静态数据成员与类的一般数据成员的不同点。
如何初始化类的静态数据成员?
如何初始化类的常数据成员?
答:
类的静态数据成员在生成对象以前已经存在,类的一般数据成员只有在利用类生成对象时才生成。
类的静态数据成员一般在定义类的界面之外(后)采用赋值法初始化(如课本121页例4-16中的intPerson:
:
total=0;)。
类的常数据成员在构造函数的圆括号()之后采用初始化列表法初始化(课本136页2.常数据成员例4-25)。
5.类的非静态数据成员与静态成员、成员函数在存储分配上有什么不同?
类的非静态数据成员各个对象的存储各自分配,而所有同类对象的静态成员、成员函数代码共用,在存储分配上是一个存储。
静态成员在生成对象之前已经存在。
6.什么是静态类型与动态类型?
答:
声明指针或引用时所指定的基类型称为该指针或引用的静态类型。
程序运行时指针或引用实际指向或引用的对象所属的类型称为该指针或引用的动态类型。
静态类型用于编译程序检查类型的合法性,动态类型是静态类型的子类型,被运行系统用于动态绑定。
7.什么是静态绑定?
什么是动态绑定?
如何实现运行时的多态?
将函数调用与其相应的函数体实现代码相关联的过程称为绑定。
静态绑定又称为早期绑定,意味着调用哪个版本的函数在程序编译时已确定。
静态绑定具有执行效率高、占用内存少等优点,但缺乏灵活性。
动态绑定又称为晚期绑定,意味着调用哪个版本的函数在程序运行时才确定。
C++语言的动态绑定通过继承与虚函数实现,具体可以声明一个含有纯虚函数的抽象基类(课本227页例7-5)或含有虚函数的基类和由此(抽象)基类公有派生的几个子类,纯虚函数(或虚函数课本221页例7-2)在不同子类中有不同的具体实现(一般都被定义为公有函数)。
一般是先声明一个祖先类指针、然后将祖先类或其派生类对象的地址赋值给这个祖先类指针,再通过指针来调用虚函数实现动态绑定:
即指针指向哪个对象就执行该对象所属类型的虚函数;当然也可以定义函数的参数为祖先类的引用、当调用函数时通过将祖先类对象或其派生类对象作为实际参数传递给函数的引用参数来实现成员函数调用的动态绑定;动态绑定具有允许程序有更大灵活性,提高程序可重用性、可扩充性等优点,但降低了程序执行效率和程序的可理解性。
.
8.在什么时候执行构造函数?
什么时候执行析构函数?
答:
类在生成对象的时候执行构造函数;当对象生命期结束的时候执行析构函数。
9.请叙述构造子类对象时构造函数的执行顺序
答:
在生成子类对象时构造函数的执行顺序是:
先父后子,先虚后实,先左后右,多路虚只长一次;父类的构造函数执行完毕后,内嵌对象的构造函数按照声明顺序执行,最后执行子类的构造函数。
10.实现动态联编方式的三个前提是什么?
答:
第一,声明一个含有纯虚函数的抽象基类(课本227页例7-5)或含有虚函数的基类和由此(抽象)基类公有派生的几个子类,纯虚函数(或虚函数课本221页例7-2)在不同子类中有不同的具体实现(一般都被定义为公有函数);第二,类之间应当满足赋值兼容性规则(课本206页);第三,通过成员函数调用或通过指针引用来调用虚函数。
(课本221页)
11.程序员在什么情况下必须设计构造函数?
在什么情况下必须设计析构函数?
答:
当类含有指针成员(一般构造函数中有动态内存分配语句new,或当数据成员较多、含有数组成员)时必须设计构造函数和析构函数(析构函数中含有delete语句去释放new分配的内存)。
12.序员在什么情况下必须设计(复制)COPY构造函数?
当对象需要占用堆资源时(具体就是对象含有指针成员时),需要在类中显式定义复制构造函数,对资源进行深复制。
见课本第118页1—3行
13.友元函数是类的成员函数吗?
友元函数能否直接访问声明该函数为友元的类的私有成员?
答:
友元函数不是类的成员函数;友元函数可以通过对象直接访问声明该函数为友元的类的私有成员。
14.面向对象的三大特性是什么?
答:
三大特性是:
封装性,继承性,多态性.
15.请说明概括(泛化)和特化的含义:
概括(泛化):
从某些具有共性的对象或类中提取抽象出高一层的类;在分类的基础上进行。
特化:
继承高层次的类,并添加一些属性和方法,在构造子类时使用。
16.请说明是么是深拷贝?
什么是浅拷贝?
如何实现深拷贝?
当对象占有资源时(对象之外的内存),在用一个已知的对象去初始化另一个对象时,只是复制对象本身,而两个对象共用对象之外的资源,这种情况叫浅拷贝;如果对对象之外的资源也进行拷贝(即两个对象分别占有不同的资源)就叫深拷贝。
实现深拷贝需要定义拷贝构造函数,并在拷贝构造函数中使用NEW运算分配资源等。
17.类摸版与抽象类的相同点与主要区别是什么?
答:
类属类分为:
类摸版与抽象类。
它们的相同点是都不能拿来直接生成对象,主要区别是类摸版是用来对类属参数用类型参数实例化后生成具体的类,然后可以生成对象;而抽象类主要是用来继承的,用于实现动态绑定。
包容数据结构用类摸版(参数化类)机制实现,类摸版实现的类属类是无约束类属类。
C++语言的受约束类必须采用抽象类、继承与动态绑定实现,如多态数据结构就是受约束类属类。
18.什么是包容数据结构?
如果一个数据结构(如:
堆栈,链表等)中可以存放多种(无关的)类型的对象,这个数据结构被称为包容数据结构。
包容数据结构可以存放完全无关的类的对象。
包容数据结构用类摸版(参数化类)机制实现。
19.什么是无约束类属类?
类摸版的实际参数类型不要求有共同祖先,类模版中的操作与类属参数之间基本上没有关联。
类摸版实现的类属类是无约束类属类。
C++的模版机制无法支持受约束类。
20.什么是多态数据结构(受约束类属类,异质数据结构)?
如果一个数据结构(如:
堆栈,链表等)中可以存放多种类型的对象,但这些类型必须是从共同的基类派生出来的类。
多态数据结构用虚函数、抽象类与继承机制实现。
21.类摸版的界面和它的类属成员函数一定要放在一个文件吗?
是,必须放在一个文件,因为编译程序并不为类属类的定义生成任何代码,仅当类属类实例化时才产生真正代码。
未实例化的类属类(类模版)定义没有加入目标文件(.OBJ)中,当建立的可重用类库(.LIB)中包含类属类时会出现程序连接错误。
22.假如已经定义了一个堆栈类摸版:
template
classSTACK{
public:
……
private:
structNODE{
ELEMENT_TYPEelement;
NODE*link;
};
NODE*top;
};
……
在主程序中有下列语句,
…
STACKint_stack;
…
当执行该语句时生成对象int_stack要分为几个步骤?
答:
首先实例化摸版STACK生成一个具体的类,类名为STACK,然后利用该类创建一个整数类型堆栈的对象实例int_stack。
(理解看课本251—259页)
23.写出(课本251页例8-2)下面程序的运行结果。
#include
template
TMax(Tx,Ty)
{
return(x>y)?
x:
y;
}
voidmain()
{
intx1,y1;
floatx2,y2;
doublex3,y3;
cout<<”请输入2个整形数据,用空格分隔:
”<cin>>x1>>y1;
cout<<”Themaxofx1,y1is:
”<cout<<”请输入2个实型数据,用空格分隔:
”<cin>>x2>>y2;
cout<<”Themaxofx2,y2is:
”<cout<<”请输入2个双精度型数据,用空格分隔:
”<cin>>x3>>y3;
cout<<”Themaxofx3,y3is:
”<}
答:
程序运行结果见课本251页。
24.全局对象,全局静态对象,局部静态对象的构造函数分别在在什么时候被调用?
答:
全局对象,全局静态对象的构造函数在main()函数执行之前被调用;局部静态对象的构造函数在程序第一次执行到相应的生命语句时才被调用。
静态对象只被初始化一次。
25.使用常成员函数时要注意几点?
答:
注意以下几点:
(1)const是函数类型的一个组成部分,因此在函数的实现部分也要使用关键字const。
(2)常成员函数不能修改对象的数据成员,也不能调用该类中没有用关键字const修饰的成员函数。
(3)关键字const参与区分函数重载。
(4)通过常对象只能调用它的常成员函数,而不能调用其他成员函数。
二、请指出下列程序中的错误语句,并在语句旁边写出错误原因。
(分)
1.
#include
voidmain()
{
intf=6;
int*a=&f;
constint*p=a;
*p=11;//p为指向常量的指针,试图用*p去修改p所指向空间的值不行。
f=7;
*a=8;
//以上两句对,这里只是固定*p,并不固定p、f和*a
cout<<"*p="<<*p<intd=7;//p可以指向别处,如下:
p=&d;
cout<<"*p="<<*p<inte=13;
int*constpp=&e;//const靠近pp,只是指针pp被固定,*pp所指空间的内容可以被修改。
pp=&f;//pp不能被修改,试图修改pp为错句,即指针pp只能指向变量e(固定的空间地址)
cout<<"*p="<<*p<cout<<"a="<}
2.
#include
voidmain()
{
charx[5]="abcdefghij";//错误,数组赋值下标越界。
arrayboundsoverflow
char*xx=&("abcdefghij");
cout<x="baiyun";//错误,数组名是常量;leftoperandmustbel-value
cout<}
三根据以下代码画出一个类的继承关系图(分);
#include
#include
classA
{
private:
intA_private;
protected:
intA_protected;
public:
A(intrr1=0,intrr2=0)
{
cout<<"thisisclassA`sconstrue."<A_private=rr1;
A_protected=rr2;
}
voidget_rot(){cout<};
classB:
publicA
{
private:
intB_private;
protected:
intB_protected;
public:
B(intp1=0,intp2=0,intp3=0,intp4=0):
A(p1,p2)
{
cout<<"thisisclassB`sconstrue."<B_private=p3;
B_protected=p4;
};
};
classC:
virtualpublicB//类B为虚基类
{
private:
intC_private;
protected:
intC_protected;
public:
C(intp1=0,intp2=0,intp3=0,intp4=0,intp5=0,intp6=0):
B(p1,p2,p3,p4)
{
C_private=p5;
C_protected=p6;
cout<<"thisisclassC`sconstrue."<};
intgetC_private(){returnC_private;}
intgetC_protected(){returnC_protected;}
};
classD:
virtualpublicB//B类为虚基类
{
protected:
intD_protected;
public:
D(intp1=0,intp2=0,intp3=0,intp4=0,intp5=0):
B(p1,p2,p3,p4)
{
D_protected=p5;
cout<<"thisisclassD`sconstrue."<};
intgetD_protected(){returnD_protected;}
};
classE:
publicC,publicD
{
private:
intE_pivate;
public:
E(intp1=0,intp2=0,intp3=0,intp4=0,intp5=0,intp6=0,intp7=0,intp8=0):
C(p1,p2,p3,p4,p5,p6),D(p1,p2,p3,p4,p7)
{
E_pivate=p8;
cout<<"thisisclassE`sconstrue."<};
};
三、请画图并用文字说明画线部分语句执行的过程。
(分)
#include
classcoord{
intx,y;
public:
coord(){x=0;y=0;}
coord(inti,intj){x=i;y=j;}
coord(constcoord&obj){x=obj.x;y=obj.y;cout<<”CopyConstuctingwascalled./n”;}
voidget_xy(int&i,int&j){i=x;j=y;}
coordoperator+(coordob2);
};
coordcoord:
:
operator+(coordob2)
{
coordtemp;
temp.x=x+ob2.X;//x+ob2.X的x为左操作数的属性域,通过this指针找//到对象的属性域。
temp.y=y+ob2.y;//道理同上一行。
returntemp;//产生一个无名临时对象作为函数的返回值后,遇到}
}//析构对象temp,退出+函数。
main()
{
coordo1(10,10),o2(15,23),o3;
intx,y;
o3=o1十o2;//调用operator+()进行两对象相加,返回无名临时对象,仍是coord对象;因而可以赋值给o3,但要调用拷贝构造函数,构造o3后,然后释放临时无名对象。
//同理,语句o3=ol+o2+o3;也是合法的
o3.get_xy(x,y);
//调用对象o3的成员函数,(x,y)为引用实参,把main()中的两个变量intx,y的地址传给对象o3的成员函数,实现把对象的属性值通过intx,y带出。
//用(o1十o2).geL_xy(x,y);也是合法的,通过临时对象实现。
cout<<"(o1+o2)X:
"<"<return0;
}
四、根据下列代码,指出子类B所含的成员及成员的访问控制属性(分)。
1、
classA{
B所含数据成员
A:
:
a不可访问
A:
:
b保护
B:
:
d私有
B:
:
e保护
private:
inta;
protected:
intb;
public:
A(inta1=0,intb1=0){a=a1;b=b1}
B所含成员函数
A:
:
getA_a()公有
A:
:
getA_b()公有
B:
:
B()公有
B:
:
getB_d()公有
B:
:
getA_e()公有
intgetA_a(){returna;}
intgetA_b(){returnb;}
};
classB:
publicA{
private:
intd;
protected:
inte;
public:
B(intd1=0,inte1=0,inta1=0,intb1=0):
A(a1,b1)
{d=d1;e=e1}
intgetB_d(){returnd;}
intgetA_e(){returne;}
};
……(答案见课本192)
2、
classA{
private:
inta;
protected:
intb;
public:
A(inta1=0,intb1=0){a=a1;b=b1}
intgetA_a(){returna;}
intgetA_b(){returnb;}
};
classB:
privateA{
private:
intd;
protected:
inte;
public:
B(intd1=0,inte1=0){d=d1;e=e1}
intgetB_d(){returnd;}
intgetA_e(){returne;}
};
五、请分步骤写出main()中赋值语句:
ob=input();执行的过程中构造函数与析构函数执行的顺序,并对后边有//的语句加上语句说明。
#include
#include
#include
chartemp1[]="ILoveYou!
";
classsamp
{
char*s;
public:
samp(){s=NULL;cout<<"默认构造函数被调用。
"<samp(charss[])
{//含有字符串成员的类的构造函数必须这样写,因为字符串不能直接赋值。
intl=strlen(ss);
s=newchar[l+1];//+1是为了在字符串尾增加一个结束标志\0。
strcpy(s,ss);
cout<<"构造函数被调用。
"<}
~samp(){if(s)delete[]s;cout<<"freeingchar*s\n";};
samp&operator=(samp&ob1)
{
cout<<"赋值号=被调用"<deletes;//防止产生内存垃圾。
intl=strlen(ob1.s);
s=newchar[l+1];
strcpy(s,ob1.s);
return*this;
};
samp(samp&ob1)//拷贝构造函数,当用对象初始化对象时调用。
{
cout<<"拷贝构造函数被调用"<intl=strlen(ob1.s);//计算字符串大小
s=newchar[l+1];//分配一个字符数组,首地址付给s,+1是为了存储字符串结束标志\0,
strcpy(s,ob1.s);//是头文件的函数,
//功能是把ob1.s所指字符串拷贝到指针s所指空间。
//有关字符数组的拷贝必须用strcpy,或自己写一段循环程序完成。
};
voidshow(){
if(s==NULL){cout<<"对象数据域为空"<else{cout<<"对象的数组成元"<
voidset(char*str);//类samp的成员函数,目的是分配字串空间并从键盘读入字符串。
};
voidsamp:
:
set(char*str)//分配字串空间并从键盘读入字符串
{
s=newchar[strlen(str)+1];//分配字串空间,+1为存放字符串结束标志\0
if(!
s)//如果s='\0',则!
s为非零即为真,也即上一句存储分配不成功。
{
cout<<"Allocationerror.\n";//显示存储分配失败。
exit
(1);//exit
(1)包含在头文件内。
表示返回到操作系统。
}
strcpy(s,str);//是头文件的函数,功能是把指针str所指字符串拷贝到指针s所指空间。
}
sampinput()//返回samp型对象的普通函数(不是类的成员函数)。
{
chars[80];//分配一个大小为80个字节的字符数组,s是数组名,不是类的成员。
samptemp;//默认构造函数被调用,产生一个samp类对象temp,其作用域是局部的。
cout<<"Enterastring:
";//cout为的函数,<<为流符号
//此句在显示器显示Enterastring:
为下句作提示
cin>>s;//cin为的函数,>>为流符号,此句作用是把通过键盘输入
//的字符串放入指针常量s(s是数组名,不是类的成员)所指的一片存储,
//注意这里不是把一个字符串付给另一个字符串。
temp.set(s);//调用对象temp的成员函数;samp:
:
set(char*str),实参是s
//功能是把(字符串)地址常量s所指字符串拷贝到对象成员指针s所指空间。
returntemp;//返回对象temp的拷贝、一个临时无名对象,而不是temp本