return0;
}
(2)请根据上述程序简要回答问题。
(2.1)(3分)复制构造函数中为什么首先要将指针x初始化为NULL?
与其后执行的语句有什么关系?
复制构造函数体中使用了赋值运算符函数,而赋值运算符函数中若x不为NULL,则执行delete[]操作。
若不将x初始化为NULL,其值可能不可预知,且可能不为NULL,直接执行delete[]将出错。
(2.2)(3分)为什么将运算符函数operator<<重载为友元函数?
因为运算符函数operator<<为双目运算,且第一个操作数不是本类的对象,不能将其定义为成员函数,而将其定义成类的友元函数。
(2.3)(4分)在主函数测试时,为什么可以使用双方括号(如a[i][j]),请分别解释其中第一个方括号、第二个方括号的理由。
表达式a[i][j]中,第一个方括号为该类重载的方括号运算符函数,它返回所谓的“行地址”的地址值;第二个方括号为系统提供的根据地址访问目标变量运算。
三、阅读程序写出运行结果及修改函数(共26分)
(1)有如下头文件Complex.h,其中设计了一个复数类
#ifndefCOMPLEX_H//Complex.h头文件
#defineCOMPLEX_H
#include
usingnamespacestd;
classComplex
{
private:
doublere,im;//复数的实部、虚部
public:
Complex(doublereal=0,doubleimag=0):
re(real),im(imag){}
doubleReal()const{returnre;}
doubleImag()const{returnim;}
voidSet(doublereal,doubleimag){re=real;im=imag;}
friendostream&operator<<(ostream&out,constComplex&c)
{
out<<"("<returnout;
}
friendistream&operator>>(istream&in,Complex&c)
{
doublereal,imag;
charstr[100];
in.getline(str,100,'(');//读到左圆括号为止
in.getline(str,100,',');//读到逗号为止
real=atof(str);//将C-字符串转换成浮点数
in.getline(str,100,')');//读到右圆括号为止
imag=atof(str);//将C-字符串转换成浮点数
c.Set(real,imag);//设置复数
returnin;
}
Complexoperator+(constComplex&c)
{
Complextemp;
temp.re=re+c.re;
temp.im=im+c.im;
returntemp;
}
Complex&operator++()
{
re++;//复数增1(仅实部增1)
return*this;
}
Complexoperator++(int)
{
Complextemp(*this);
re++;
returntemp;
}
Complex&operator*=(constComplex&c)
{
Complextemp(*this);
re=temp.re*c.re-temp.im*c.im;
im=temp.re*c.im+temp.im*c.re;
运行结果(1.1)
(0,0)(2,0)(2,3)
(4,3)
(4,0)
(-5,-9)
return*this;
}
};
#endif
(1.1)(4分)请指出下面的程序的运行结果
#include"Complex.h"
intmain()
{
Complexc1,c2=2,c3(2,3);
cout<c1=c2+c3;
c2*=c2;//请严格按上述operator*=函数计算
c3*=c3;//请注意(2+3i)(2+3i)=-5+12i并不等于-5-9i
cout<cout<cout<return0;
}
(1.2)(4分)从上述运行结果可见,重载运算符*=函数的定义中存在计算错误,请改正。
由此可见重载双目运算符时,需要特别注意两个操作数为同一对象时的情形。
Complex&operator*=(constComplex&c)
{
Complextemp;
temp.re=re*c.re-im*c.im;
temp.im=re*c.im+im*c.re;
*this=temp;
return*this;
}
(1.3)(4分)请指出下面的程序的运行结果
#include"Complex.h"
intmain()
{
Complexc(2,3),d(c);
运行结果(1.3)
(3,3)(3,3)
(2,3)(3,3)
(4,3)(4,3)
(2,3)(3,3)
cout<<++c<<'\t';//此处无换行
cout<cout<cout<c.Set(2,3);
d.Set(2,3);
cout<<++++c<<'\t';//此处无换行
cout<cout<cout<return0;
}
(1.4)(4分)请指出下面的程序的运行结果
#include"Complex.h"
intmain()
运行结果(1.4)
(2,3)
(2,3)
(4,5)
(2,3)
{
Complexc1,c2(2,3),*p;
cout<<(c1=c2)<cout<p=newComplex(4,5);
cout<<*p<deletep;
p=&c1;
cout<<*p<return0;
}
(2)有如下程序
#include
usingnamespacestd;
classBase
{
protected:
intx,y;
public:
Base(inta,intb=1):
x(a),y(b){}
virtual~Base(){}
virtualvoidShow(ostream&out)const=0;
};
ostream&operator<<(ostream&out,constBase&b)
{
b.Show(out);
returnout;
}
classDerived:
publicBase
{
private:
intz;
public:
Derived(inta,intb,intc):
Base(a,b),z(c){}
Derived&operator+=(constDerived&d)
{
x+=d.x;
y+=d.y;
z+=d.z;
return*this;
}
voidShow(ostream&out)const
{
out<<"[("<}
};
classRationalNumber:
publicBase//派生有理数类
{
public:
RationalNumber(inta,intb=1):
Base(a,b){}
RationalNumber&operator+=(constRationalNumber&r)
{//有理数(分数)相加
inttemp=x*r.y+y*r.x;
y*=r.y;
x=temp;
return*this;
}
voidShow(ostream&out)const
{
out<}
};
运行结果
[(3,2),1]
[(4,5),6]
[(7,7),7]
3/2
2/3
13/6
intmain()
{
Derivedd1(3,2,1),d2(4,5,6);
RationalNumberr1(3,2),r2(2,3);
cout<cout<d1+=d2;
cout<cout<cout<r1+=r2;
cout<return0;
}
(2.1)(6分)请指出上述程序的运行结果(填入上面的表格中)。
(2.2)(2分)简要回答。
基类Base中有纯虚函数,故Base为抽象类。
不能创建Base类的对象。
能否声明Base类的引用(例如用派生类的对象初始化引用)?
能否定义目标数据类型为基类的指针变量(例如用派生类对象的地址对其初始化或赋值)?
答:
皆能。
(2.3)(2分)简要回答。
派生类Derived或RationalNumber类的构造函数中为什么要显式地使用冒号语法规则对基类的数据成员进行初始化。
答:
因为基类Base没有默认的构造函数。
四、设计类(每个函数4分,共24分)一个IPv4(InternetProtocolversion4)地址用一个32位(即unsignedlong型)整数表示。
常用点分十进制(dotted-decimalnotation)的形式,即其4个字节被分开用十进制写出,中间用点分隔。
例如某IPv4地址的点分十进制表示为192.168.100.1,则其对应的32位整数为(c0a86401)16即3232261121=((192*256+168)*256+100)*256+1。
请完成如下的成员函数或友元函数的定义,使程序输出指定的结果。
#include
#include
usingnamespacestd;
classIPv4
{
private:
inta,b,c,d;//利用4个整数记录被点分的数
public:
IPv4(char*ip="127.0.0.1")//构造函数
{
inti;
a=b=c=d=0;
for(i=0;ip[i]!
='.';i++)
if(ip[i]>='0'&&ip[i]<='9')
a=10*a+ip[i]-'0';
for(i++;ip[i]!
='.';i++)//请注意此处<表达式1>为i++
if(ip[i]>='0'&&ip[i]<='9')
b=10*b+ip[i]-'0';
for(i++;ip[i]!
='.';i++)
if(ip[i]>='0'&&ip[i]<='9')
c=10*c+ip[i]-'0';
for(i++;ip[i]!
='\0';i++)
if(ip[i]>='0'&&ip[i]<='9')
d=10*d+ip[i]-'0';
}
int&operator[](intk)//重载方括号运算符,可返回各点分部分
{
switch(k)
{
case0:
returna;
case1:
returnb;
case2:
returnc;
default:
returnd;
}
}
friendbooloperator>(constIPv4&ip1,constIPv4&ip2)
{returnip1.get()>ip2.get();}//get函数见后
friendbooloperator>=(constIPv4&ip1,constIPv4&ip2)
{returnip1.get()>=ip2.get();}
friendbooloperator<(constIPv4&ip1,constIPv4&ip2)
{returnip1.get()friendbooloperator<=(constIPv4&ip1,constIPv4&ip2)
{returnip1.get()<=ip2.get();}
friendbooloperator==(constIPv4&ip1,constIPv4&ip2)
{returnip1.get()==ip2.get();}
friendbooloperator!
=(constIPv4&ip1,constIPv4&ip2)
{returnip1.get()!
=ip2.get();}
IPv4&operator--()//重载前减量运算符
{
d--;
if(d==-1){d=255;c--;}
if(c==-1){c=255;b--;}
if(b==-1){b=255;a--;}
if(a==-1){a=255;}
return*this;
}
IPv4operator--(int)//重载后减量运算符
{
IPv4temp(*this);
--(*this);
returntemp;
}
IPv4operator+(intn)//重载加号运算符,实现IP地址与整数相加
{
IPv4temp;
temp.set(get()+n);
returntemp;
}
IPv4&operator+=(intn)//IPv4对象与整数迭代相加
{
*this=*this+n;
return*this;
}
IPv4operator-(intn)//重载减号运算符,实现IP地址与整数相减
{
return*this+(-n);
}
IPv4&operator-=(intn)//IPv4对象与整数迭代相减
{
*this=*this+(-n);
return*this;
}
intoperator-(constIPv4&ip)//两个IPv4对象相减
{
returnget()-ip.get();
}
//请根据要求定义成员函数或友元函数(请注意:
这是在类体中)。
//①请定义如下函数,返回由a,b,c,d构成的一个32位的无符号长整型数值,
//该函数是前面诸多成员函数的基础。
unsignedlongget()const
{
unsignedlongintn;
n=d+256*(c+256*(b+256UL*a));
returnn;
}
//②请定义如下函数,根据给定的32位无符号长整型数值设置数据成员a,b,c,d。
voidset(unsignedlongn)
{
d=n%256;
c=n/256%256;
b=n/256/256%256;
a=n/256/256/256%256;
}
//③请重载前增量运算符(++)使其为下一个IP(参见测试程序及运行结果)
IPv4&operat