资源描述
《C++程序设计根底》〔第4版〕〔下〕
习题与解答
第6章 类与对象
6.1 选择题
1.假设有以下说明,那么在类外使用对象objX成员的正确语句是〔 C 〕。
class X
{
int a;
void fun1();
public:
void fun2();
};
X objX;
〔A〕objX.a=0;〔B〕objX.fun1();〔C〕objX.fun2();〔D〕X::fun1();
2.假设有以下说明,那么对n的正确访问语句是〔 B 〕。
class Y
{
//… ;
public:
static int n;
};
int Y::n;
Y objY;
〔A〕n=1;〔B〕Y::n=1;〔C〕objY::n=1;〔D〕Y->n
3.假设有以下类Z说明,那么函数fStatic中访问数据a错误的选项是〔 C 〕。
class Z
{
static int a;
public:
static void fStatic(Z&);
};
int Z::a=0;
Z objZ;
〔A〕void Z::fStatic() { objZ.a =1; }
〔B〕void Z::fStatic() { a = 1; }
〔C〕void Z::fStatic() { this->a = 0; }
〔D〕void Z::fStatic() { Z::a = 0; }
4.假设有以下类W说明,那么函数fConst的正确定义是〔 A 〕。
class W
{
int a;
public:
void fConst(int&) const;
};
〔A〕void W::fConst( int &k )const { k = a; }
〔B〕void W::fConst( int &k )const { k = a++; }
〔C〕void W::fConst( int &k )const { cin >> a; }
〔D〕void W::fConst( int &k )const { a = k; }
5.假设有以下类T说明,那么函数fFriend的错误定义是〔 C 〕。
class T
{
int i;
friend void fFriend( T&, int );
};
〔A〕void fFriend( T &objT, int k ) { objT.i = k; }
〔B〕void fFriend( T &objT, int k ) { k = objT.i; }
〔C〕void T::fFriend( T &objT, int k ) { k += objT.i; }
〔D〕void fFriend( T &objT, int k ) { objT.i += k; }
6.在类定义的外部,可以被访问的成员有〔 C 〕。
〔A〕所有类成员〔B〕private或protected的类成员
〔C〕public的类成员〔D〕public或private的类成员
7.关于this指针的说确的是〔 C 〕。
〔A〕this指针必须显式说明〔B〕定义一个类后,this指针就指向该类
〔C〕成员函数拥有this指针〔D〕静态成员函数拥有this指针
8.说明一个类的对象时,系统自动调用〔 B 〕;撤销对象时,系统自动调用〔 C 〕。
〔A〕成员函数〔B〕构造函数〔C〕析构函数〔D〕友元函数
9.下面对构造函数的不正确描述是〔 C 〕。
〔A〕用户定义的构造函数不是必须的〔B〕构造函数可以重载
〔C〕构造函数可以有参数,也可以有返回值〔D〕构造函数可以设置默认参数
10.下面对析构函数的正确描述是〔 C 〕。
〔A〕系统在任何情况下都能正确析构对象〔B〕用户必须定义类的析构函数
〔C〕析构函数没有参数,也没有返回值〔D〕析构函数可以设置默认参数
11.关于类的静态成员的不正确描述是〔 D 〕。
〔A〕静态成员不属于对象,是类的共享成员
〔B〕静态数据成员要在类外定义和初始化
〔C〕静态成员函数不拥有this指针,需要通过类参数访问对象成员
〔D〕只有静态成员函数可以操作静态数据成员
12.在以下选项中,〔 C 〕不是类的成员函数。
〔A〕构造函数〔B〕析构函数〔C〕友元函数〔D〕复制构造函数
13.下面对友元的错误描述是〔 D 〕。
〔A〕关键字friend用于声明友元
〔B〕一个类中的成员函数可以是另一个类的友元
〔C〕友元函数访问对象的成员不受访问特性影响
〔D〕友元函数通过this指针访问对象成员
14.假设class B中定义了一个class A的类成员A a,那么关于类成员的正确描述是〔 C 〕。
〔A〕在类B的成员函数中可以访问A类的私有数据成员
〔B〕在类B的成员函数中可以访问A类的保护数据成员
〔C〕类B的构造函数可以调用类A的构造函数做数据成员初始化
〔D〕类A的构造函数可以调用类B的构造函数做数据成员初始化
6.2 阅读以下程序,写出运行结果
1.
#include<iostream>
using namespace std;
class A
{
public :
int f1();
int f2();
void setx( int m ) { x = m; cout << x << endl; }
void sety( int n ) { y = n; cout << y << endl; }
int getx() { return x; }
int gety() { return y; }
private :
int x, y;
};
int A::f1()
{ return x + y; }
int A::f2()
{ return x - y; }
int main()
{
A a;
a.setx( 10 ); a.sety( 5 );
cout << a.getx() << '\t' << a.gety() << endl;
cout << a.f1() << '\t' << a.f2() << endl;
}
【解答】
10
5
10 5
15 5
2.
#include<iostream>
using namespace std;
class T
{
public :
T( int x, int y )
{
a = x; b = y;
cout << "调用构造函数1." << endl;
cout << a << '\t' << b << endl;
}
T( T &d )
{
cout << "调用构造函数2." << endl;
cout << d.a << '\t' << d.b << endl;
}
~T() { cout << "调用析构函数."<<endl; }
int add( int x, int y = 10 ) { return x + y; }
private :
int a, b;
};
int main()
{
T d1( 4, 8 );
T d2( d1 );
cout << d2.add( 10 ) << endl;
}
【解答】
调用构造函数1.
4 8
调用构造函数2.
4 8
20
调用析构函数.
调用析构函数.
3.
#include<iostream>
using namespace std;
class T
{
public:
T(int x) { a=x; b+=x; };
static void display(T c) { cout<<"a="<<c.a<<'\t'<<"b="<<c.b<<endl; }
private:
int a;
static int b;
};
int T::b=5;
int main()
{
T A(3), B(5);
T::display(A);
T::display(B);
}
【解答】
a=3 b=13
a=5 b=13
4.
#include<iostream>
using namespace std;
#include<cmath>
class Point
{
public :
Point( float x, float y )
{ a = x; b = y; cout<<"点( "<<a<<", "<<b<<" )"; }
friend double d( Point &A, Point &B )
{ return sqrt((A.a-B.a)*(A.a-B.a)+(A.b-B.b)*(A.b-B.b)); }
private:
double a, b;
};
int main()
{
Point p1( 2, 3 );
cout << " 到";
Point p2( 4, 5 );
cout << "的距离是:" << d( p1,p2 ) << endl;
}
【解答】
点〔3,4〕到点〔4,5〕的距离是:2.82843
5.
#include<iostream>
using namespace std;
class A
{
public :
A() { a = 5; }
void printa() { cout << "A:a = " << a << endl; }
private :
int a;
friend class B;
};
class B
{
public:
void display1( A t )
{ t.a ++; cout << "display1:a = " << t.a << endl; };
void display2( A t )
{ t.a --; cout << "display2:a = " << t.a << endl; };
};
int main()
{
A obj1;
B obj2;
obj1.printa();
obj2.display1( obj1 );
obj2.display2( obj1 );
obj1.printa();
}
【解答】
A:a = 5
display1:a = 6
display2:a = 4
A:a = 5
6.
#include<iostream>
using namespace std;
class A
{
public:
A(int x):a(x=0){ }
void getA(int &A) { A=a; }
void printA() { cout<<"a="<<a<<endl; }
private:
int a;
};
class B
{
public:
B(int x, int y):aa(x=0) { b = y; }
void getAB(int &A, int &outB) { aa.getA(A); outB=b; }
void printAB() { aa.printA(); cout<<"b="<<b<<endl; }
private:
A aa;
int b;
};
int main()
{
A objA;
int m=5;
objA.getA(m);
cout<<"objA.a="<<m<<endl;
cout<<"objB:\n";
B objB;
objB.getAB(12,56);
objB.printAB();
}
【解答】
objA:a=5
objB:
a=12
b=56
6.3 思考题
1.结构与类有什么区别?如果把程序中定义结构的关键字struct直接改成class,会有什么问题?用教材中的一个例程试试看,想一想做什么修改能使程序正确运行?
【解答】
结构是数据的封装,类是数据和操作的封装。可以把结构看成是类的特例。结构和类都可以用关键字struct或class定义。区别是,struct定义的结构或类的全部成员都是公有的,用class定义的结构或类不做声明的成员是私有的。
假设把struct改成class,只需要把全部成员定义为public就可以了。
2.有说明:
class A
{
int a;
double x;
public:
funMember();
};
A a1, a2, a3;
编译器为对象a1、a2和a3开辟了什么存空间?它们有各自的funMember函数的副本吗?C++通过什么机制调用类的成员函数?
【解答】
开辟的存储空间有a1.a, a1.x, a2.a, a2.x, a3.a, a3.x。各对象没有funMember函数的副本,C++通过this指针调用成员函数。
3.C++提供了系统版本的构造函数,为什么还需要用户自定义构造函数?编写一个验证程序,说明自定义构造函数的必要性。
【解答】
类的默认构造函数可以建立根本类型数据成员的存储空间。基于以下两个原因,需要用户定义构造函数:
〔1〕对数据成员的值做指定初始化;
〔2〕类的数据是由指针管理的堆。
程序略。
4.试从定义方式、访问方式、存储性质和作用域4个方面来分析类的一般数据成员和静态数据成员的区别,并编写一个简单程序验证它。
【解答】
定义方式
访问方式
存储性质
作用域
一般数据成员
类中定义
对象.数据成员
局部数据
由访问属性public, protected, private决定
静态数据成员
类中声明,类外定义
对象.数据成员
类::数据成员
全局数据
程序略。
5.试从定义方式、调用方式两个方面来分析常成员函数、静态成员函数和友元函数的区别。考察例6-15,假设class Goods的指针域:
Goods * next;
被声明为私有〔private〕成员,程序会出现什么错误?做什么最小修改能使程序正确运行?
【解答】
定义方式
调用方式
常成员函数
函数原型以const做后缀
this指针被约束为指向常量的常指针
与一般成员函数调用形式一样
对数据成员只读
静态成员函数
以static做函数原型前缀
没有this指针
通过类或对象调用
用于操作静态数据成员
友员函数
以friend做函数原型前缀
没有this指针
通过参数访问对象
可以访问对象的不同属性的成员
在例6-15中,假设把next声明为私有数据成员,只须把有关指针操作的函数定义为友员函数就可以了:
friend void purchase( Goods * &f, Goods *& r, int w );
friend void sale( Goods * & f , Goods * & r );
6.设有:
class M
{
public: int a;
};
class N
{
public:
M m;
int b;
void fun()
{ /*…*/ }
};
int main()
{
N n;
N *p = &n;
/*…*/
}
描述在N::fun中如何访问M类的数据成员a?在main函数中又如何访问对象n的全部数据成员?
【解答】
在N::fun中访问M类的数据成员a的形式是: m.a
在main函数中访问M类的数据成员的形式是: n.b,n.m.a
6.4 编程题
1.定义一个Book〔图书〕类,在该类定义中包括以下数据成员和成员函数。
数据成员: bookname〔书名〕、price〔价格〕和number〔存书数量〕。
成员函数: display()显示图书的情况;borrow()将存书数量减1,并显示当前存书数量;restore()将存书数量加1,并显示当前存书数量。
在main函数中,要求创建某一种图书对象,并对该图书进展简单的显示、借阅和归还管理。
【解答】
#include <iostream>
using namespace std;
class Book
{
public:
void setBook(char*,double,int);
void borrow();
void restore();
void display();
private:
char bookname[40];
double price;
int number;
};
//在类外定义Book类的成员函数
void Book::setBook(char *name, double pri, int num)
{
strcpy(bookname, name);
price=pri;
number=num;
}
void Book::borrow()
{
if (number==0 )
{
cout << "已没存书,退出!" << endl;
abort();
}
number = number - 1;
cout << "借一次,现存书量为:" << number << endl;
}
void Book::restore()
{
number = number + 1;
cout << "还一次,现存书量为:" << number << endl;
}
void Book::display()
{
cout << "存书情况:" << endl
<< "bookname:" << bookname << endl
<< "price:" << price << endl
<< "number:" << number << endl;
}
int main()
{
char flag, ch;
Book computer;
computer.setBook( "c++程序设计根底" , 32, 1000 );
computer.display();
ch = 'y';
while ( ch == 'y' )
{
cout << "请输入借阅或归还标志(b/r):";
cin >> flag;
switch ( flag )
{
case 'b': computer.borrow(); break;
case 'r': computer.restore();
}
cout << "是否继续?(y/n)";
cin >> ch;
}
computer.display();
}
2.定义一个Box〔盒子〕类,在该类定义中包括以下数据成员和成员函数。
数据成员:length〔长〕、width〔宽〕和height〔高〕。
成员函数:构造函数Box,设置盒子的长、宽和高3个初始数据;成员函数setBox对数据成员置值;成员函数volume 计算盒子的体积。
在main函数中,要求创建Box对象,输入长、宽、高,输出盒子的体积。
【解答】
#include <iostream>
using namespace std;
class BOX
{
public:
BOX( double l, double w, double h )
{ length = l;
width = w;
height = h;
}
void volume()
{ cout << "volume=" << length * width * height << endl;
}
private:
double length, width, height;
};
int main()
{
BOX box1( 1,3,5 );
box1.volume();
BOX box2( 2,4,6 );
box2.volume();
}
3.定义一个Student类,在该类定义中包括:一个数据成员〔分数score〕与两个静态数据成员〔总分total和学生人数count〕;成员函数scoretotalcount(double s) 用于设置分数、求总分和累计学生人数;静态成员函数sum()用于返回总分;静态成员函数average()用于求平均值。
在main函数中,输入某班同学的成绩,并调用上述函数求全班学生的总分和平均分。
【解答】
#include <iostream>
using namespace std;
class student
{
public:
void scoretotalcount( double s )
{
score = s;
total = total + score;
count++;
}
static double sum()
{
return total;
}
static double average()
{
return total / count;
}
private:
double score;
static double total;
static double count;
};
double student::total=0;
double student::count=0;
int main()
{
int i,n; double s;
cout << "请输入学生人数:";
cin >> n;
student stu;
for( i=1; i<=n; i++ )
{
cout << "请输入第" << i << "个学生的分数:";
cin >> s;
stu.scoretotalcount( s );}
cout << "总分:" << student::sum() << endl;
cout << "平均分:" << student::average() << endl;
}
4.定义一个表示点的结构类型Point和一个由直线方程y = ax + b确定的直线类Line。结构类型Point有两个成员x和y,分别表示点的横坐标和纵坐标。Line类有两个数据成员a和b,分别表示直线方程中的系数。Line类有一个成员函数print用于显示直线方程。友元函数setPoint(Line &l1,Line &l2)用于求两条直线的交点。在main函数中,建立两个直线对象,分别调用print函数显示两条直线的方程,并调用函数setPoint求这两条直线的交点。
【解答】
#include <iostream>
using namespace std;
struct point
{
double x; double y;
};
class line
{
public:
line( double u, double v )
{
a=u; b=v;
}
void print()
{
cout<<"y="<<a<<"x+"<<b<<endl;
}
friend point setpoint(line &l1,line &l2);
private:
double a, b;
};
point setpoint( line &l1, line &l2 )
{
point p;
p.x=( l2.b-l1.b )/( l1.a-l2.a );
p.y=( l1.a*l2.b-l2.a*l1.b)/(l1.a-l2.a );
return p;
}
int main()
{
point setp;
line l1(2,3), l2(4,5);
cout<<"直线l1: ";
l1.print();
cout<<"直线l2: ";
l2.print();
setp=setpoint( l1,l2 );
cout<<"直线l1和直线l2的交点:("<<setp.x<<","<<setp.y<<")"<<endl;
}
5.用类成员结构修改习题6.4第4小题的程序,使其实现一样的功能。定义Point类和Line类,表示点和线;定义setPoint类,包含两个Line类成员和一个表示直线交点的Point成员,并定义类中求直线交点的成员函数。编写每个类相应的成员函数和测试用的主函数。
【解答】
略。
第7章 运算符重载
7.1 选择题
1.在以下运算符中,不能重载的是〔 B 〕。
〔A〕 !〔B〕sizeof〔C〕new〔D〕delete
2.在以下关于运算符重载的描述中,〔 D 〕是正确的。
〔A〕可以改变参与运算的操作数个数〔B〕可以改变运算符原来的优先级
〔C〕可以改变运算符原来的结合性 〔D〕不能改变原运算符的语义
3.在以下函数中,不能重载运算符的函数是〔 B 〕。
〔A〕成员函数 〔B〕构造函数〔C〕普通函数 〔D〕友元函数
4.要求用成员函数重载的运算符是〔 A 〕。
〔A〕=〔B〕==〔C〕<=〔D〕++
5.要求用友元函数重载的ostream类输出运算符是〔C 〕。
〔A〕=〔B〕[]〔C〕<<〔D〕()
6.在以下关于类型转换的描述中,错误的选项是〔 A 〕。
〔A〕任何形式的构造函数都可以实现数据类型转换。
〔B〕带非默认参数的构造函数可以把根本类型数据转换成类类型对象。
〔C〕类型转换函数可以把类类型对象转换为其他指定类型对象。
〔D〕类型转换函数只能定义为一个类的成员函数,不能定义为类的友元函数。
7.2 阅读以下程序,写出运行结果
1.
#include <iostream>
using namespace std;
class T
{
public :
T()
{ a = 0; b = 0; c = 0; }
T( int i, int j, int k )
{ a = i; b =j; c = k; }
void get( int &i, int &j, int &k )
{ i = a; j = b; k = c; }
T operator* ( T obj );
private:
int a, b, c;
};
T T::operator* ( T obj )
{
T tempobj;
tempobj.a = a * obj.a;
tempobj.b = b * obj.b;
tempobj.c = c * obj.c;
return tempobj;
}
int main()
{
T obj1( 1,2,3 ), obj2( 5,5,5 ), obj3;
int a, b, c;
obj3 = obj1 * obj2;
obj3.get( a, b, c );
cout<<"( obj1*obj2 ): " <<"a = "<<a<<'\t'<<"b = "<<b<<'\t'<<"c = "<<c<<'\n';
(obj2*obj3).get( a, b, c );
cout<<"( obj2*obj3 ): " <<"a = "<<a<<'\t'<<"b = "<<b<<'\t'<<"c = "<<c<<'\n';
}
【解答】
( obj1 * obj2 ): a = 5 b = 10 c = 15
( obj2 * obj3 ): a = 25 b = 50 c = 75
2.
#include <iostream>
using namespace std;
class Vector
{
public:
Vector(){ }
Vector(int i,int j)
{ x = i; y = j; }
friend Vector operator+ ( Vector v1, Vector v2 )
{
Vector tempVector;
tempVector.x = v1.x + v2.x;
tempVector.y = v1.y + v2.y;
return tempVector;
}
void display()
{ cout << "( " << x << ", " << y << ") "<< endl; }
private:
int x, y;
};
int main()
{
Vector v1( 1, 2 ), v2( 3, 4 ), v3;
cout << "v1 = ";
v1.display();
cout << "v2 = ";
v2.display();
v3 = v1 + v2;
cout << "v3 = v1 + v2 = ";
v3.display();
}
【解答】
v1 = ( 1, 2 )
v2 = ( 3, 4 )
v3 = v1 + v2 = ( 4, 6 )
7.3 思考题
1.一个运算符重载函数被定义为成员函数或友元函数后,在定义方式、解释方式和调用方式上有何区别?可能会出现什么问题?请用一个实例说明之。
【解答】
以二元运算符为例。
运算符重载
定义
解释
调用
成员函数
Obj& operator op();
Obj operator op(object);
Obj.operator op()
ObjL.operator op(ObjR)
Obj op 或 op Obj
ObjL op ObjR
左操作数通过this指针指定,右操作数由参数传递
友员函数
friend Obj & operator op(Obj &);
friend Obj operator op(Obj,Obj);
operator op(Obj)
operator op(ObjL,ObjR)
Obj op 或 op Obj
ObjL op ObjR
操作数均由参数传递
可能会出现的问题:
〔1〕运算符的左右操作数不同,须用友员函数重载;
〔2〕当运算符的操作需要修改类对象状态时,应用成员函数重载。
〔3〕友员函数不能重载运算符 = () [] ->
必须要用友员函数重载的运算符 >> <<
程序略。
2.类类型对象之间、类类型和根本类型对象之间用什么函数进展类型转换?归纳进展类型转换的构造函数和类型转换函数的定义形式、调用形式和调用时机。
【解答】
构造函数可以把根本类型、类类型数据转换成类类型数据;类类型转换函数可以在类类型和根本数据类型之间做数据转换。
定义形式
调用形式
调用时机
构造函数
ClassX::ClassX(arg,arg1=E1,...,argn=En);
隐式调用
建立对象、参数传递时
类类型转换函数
ClassX::operator Type();
用类型符显式调用;
自动类型转换时隐式调用
需要做数据类型转换时
7.4 编程题
1.分别使用成员函数和友元函数编写程序重载运算符“+〞,使该运算符能实现两个字符串的连接。
【解答】
〔1〕 使用成员函数
#include <iostream>
#include<cstring>
using namespace std;
class s
{
public:
s(){ *str = '\0'; }
s( char *pstr ) { strcpy( str,pstr );
char *gets() { return str; }
s operator+( s obj );
private:
char str[10];
};
s s::operator+( s obj )
{
strcat( str,obj.str );
return str; //或return *this
}
int main()
{
s obj1( "Visual" ),obj2( " C++" ),obj3;
obj3 = obj1 + obj2;
cout << obj3.gets() << endl;
}
〔2〕使用友员函数
#include <iostream>
#include<cstring>
using namespace std;
class s
{ public:
s(){ *str= '\0'; }
s( char *pstr ) { strcpy( str,pstr ); }
char *gets() { return str; }
friend s operator+( s obj1,s obj2 );
private:
char str[100];
};
s operator+( s obj1,s obj2 )
{
s tempobj;
strcat( tempobj.str,obj1.str );
strcat( tempobj.str,obj2.str );
return tempobj;
}
int main()
{
s obj1( "Visual" ),obj2( " C++" ),obj3;
obj3 = obj1 + obj2;
cout << obj3.gets() << endl;
}
2.定义一个整数计算类Integer,实现短整数 +、-、*、/ 根本算术运算。要求:可以进展数据围检查〔-32 768~32 767,或自行设定〕,数据溢出时显示错误信息并中断程序运行。
【解答】
#include <iostream>
using namespace std;
class Integer
{
private:
short a;
public:
Integer (short n=0){ a=n;}
Integer operator +(Integer);
Integer operator -(
展开阅读全文