C++实验六const对象和const成员、友元
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
评语:
指导教师(签名)
年月日说明:指导教师评分后,实验报告交院(系)办公室保存。
实验6 const对象和const成员、友元1.实习目的及要求
1)掌握coust对象、coust成员函数和coust成员数据的使用;
2)掌握友元函数的定义和使用。
2.预习
1)预习coust对象及const成员的定义、使用等相关知识;2)预习友元的概念、友元成员函数、友元类等相关知识。
预习知识总结:
const
3.实验内容
3.1分析下面的程序,观察编译程序时会出现什么样的错误。
#include<iostream.h> class Sample
{
int n;
public:
Sample(int i)
{n=i;}
void setvalue(int i) {n=i;
}
void display()
{
cout<<"n="<<n<<endl; }
};
void main()
{
const Sample s(5);
s.setvalue(8);
s.display();
}
分析:明显在main部分,对象s被定义被初始化为一个对象常量,其成员数据是不可以被setvalue修改的。
编译的时候出现了如下的错误:
error C2662: 'setvalue' : cannot convert 'this' pointer from 'const class Sample' to
'class Sample &'Conversion loses qualifiers
error C2662: 'display' : cannot convert 'this' pointer from 'const class Sample' to 'class
Sample &'Conversion loses qualifiers
所以应该修改为: void main(){
const Sample s(5); Sample s1(5); s.display();
s1.setvalue(8); s1.display(); }
这样的话,s1的成员函数才可以修改成员数据的值。
3.2 编写并调试程序
1).编写一个程序求直角坐标中一个点到直线的距离。
要求设计一个点类point ,含有两个公有成员数据x 和y ,代表点的坐标。
另一个类为直线类line ,它有三个私有成员数据a ,d ,c 。
分别代表直线方程ax+by+c=0的系数。
在类line 中设计一个友元函数distance 计算一个点到直线的距离。
点(x ,y )到直线ax+by+c=0的距离distance 的计算公式如下:
请根据下面给出的部分程序编写直线类line 及其成员函数,以组成完整的程序。
#include<iostream.h> #include<math.h> class line; class point { public: float x, y; point(float i,float j) {x=i;y=j;} };
... void main() { point p1(2,4); line l1(1,2,3); cout<<"distance="<<distance(p1,l1)<<endl; }
2
2
|
|distance
b
a c by ax +++=
1.line类代码设计:
class line{
private:
float a,b,c;
public:
line (float a1=0,float b1=0,float c1=0)
{a=a1;b=b1;c=c1;}
friend double distance(point &p,line &l);
};
2.友元函数distance设计:
double line::distance(point &p,line &l){
return fabs(l.a*p.x+l.b*p.y+p.x)/(sqrt(l.a*l.a+l.b*l.b));
}
e)和题目部门代码组合并在Visual C++上编译调试,结果出现了以下问题
错误分析:友元函数是在类中用关键字friend 修饰的非成员函数,所以函数头double line::distance(point &p,line &l)明显把友元函数当成了类line 里面的成员函数,导致编译的时候出现了语法错误。
所以应该改为:
再次运行没有错误显示,链接得出结果,结果截图如下:
和预测的结果有出入,检查友元函数distance 中的语句
return fabs(l.a*p.x+l.b*p.y+p.x)/(sqrt(l.a*l.a+l.b*l.b));和公式 对比发现设计错误
return fabs(l.a*p.x+l.b*p.y+l.c)/(sqrt(l.a*l.a+l.b*l.b));得
22||distance b
a c by ax +++=
f)总的代码实现如下:
2)编写一类Cpoint表示一个点的信息。
在此基础上编写一个表示三角形的类tria,顶点为其对象成员。
编写完整的程序输出三角形的面积。
要求用用友元函数来实现三角形面积的输出。
类Cpoint是一个点(x,y)的信息,所以设计为拥有两个数据成员x,y和数据输入操作。
而tria表示的一个三角形,三角形的静态属性为三个顶点,顶点之间的距离和三角形名字;其动态属性求三角形面积并输出,求两点间距离。
类图如下:
c)代码设计:
(1)Cpoint类代码设计:
class Cpoint{
private:
float x,y;
public:
Cpoint(float a=0,float b=0){x=a;y=b;} Cpoint(Cpoint &p){x=p.x;y=p.y;} float getx(){ return x;}
float gety(){ return y;}
};
(2)tria类代码设计:
class tria{
private:
Cpoint a,b,c;
float ab,bc,ca;
double area;
public:
tria(Cpoint p1,Cpoint p2,Cpoint p3);
double length(Cpoint p1,Cpoint p2);
friend double getarea(tria &t1);
};
tria::tria(Cpoint p1,Cpoint p2,Cpoint p3):a(p1),b(p2),c(p3){}
double tria::length(Cpoint p1,Cpoint p2){
double x=double(p1.getx()-p2.getx());
double y=double(p1.gety()-p2.gety());
return sqrt(x*x+y*y);
}
(3)友元函数getarea(tria t1)的代码设计:
double getarea(tria &t1){
t1.ab= t1.length(t1.a, t1.b);
t1.bc= t1.length(t1.b, t1.c);
t1.ca= t1.length(t1.c, t1.a);
double p=(t1.ab+t1.bc+t1.ca)/2;
t1.area=sqrt(p*(p- t1.ab)*(p- t1.bc)*(p- t1.ca));
return t1.area;
}
(4)main部分代码设计
void main(){
Cpoint p1(0,0),p2(0,3),p3(4,0);
tria A(p1,p2,p3);
cout<<"三角形的面积是"<<getarea(A)<<endl;
}
d)在Visual c++中调试出现了一下错误
错误分析:仔细检查,发现getarea的友元在类里面忘了写参数,它之前的语句少了分号。
修改完之后编译,没有语法错误出现就链接,结果如下
和结果预测中的以(0,0),(3,0),(0,4)为顶点的三角形面积为6一致。
测试另外的数据(0,3.0),(2.0,0)(-2.0,0)的三角形,得结果
再次与预测结果一致。
就此认为,所编写程序是可执行的。
e)最终所求的代码为:
*4 课后练习
调试下列例程,指出程序实现的功能及程序输出结果,进一步理解coust对象及const成员的定义和使用。
1)调试程序1
# include<iostream.h>
class CSample
{
int a , b ;
public :
CSample (int x , int y )
{ a=x ; b=y; }
~ CSample ( ) { }
void PrintAB( ) ;
void PrintAB( ) const ;
const void Test ( ) { }
} ;
void CSample::PrintAB ()
{ // a=2 ;
//Test ( );
cout<<"调用函数CSample::PrintAB() \n " ; cout<<"A="<<a<<'\t'<<"B="<<b<<endl ;
}
void CSample::PrintAB () const
{ cout<<"调用函数CSample::PrintAB() const !\n " ;
cout<<"A="<<a<<'\t'<<"B="<<b<<endl ;
//a=2 ;
//Test ( );
}
void main ( )
{
CSample s1 (11 ,22 ) ;
CSample const s2(33 ,44) ;
s1.PrintAB() ;
s2.PrintAB() ;
}
a)类图和对象图分析
b)功能分析:通过常对象和一般对象的调用类成员函数的区别说明常对象只
能调用常成员函数,这也说明了const关键字可以用于对重载函数的区分,如类中:void PrintAB( ) ;void PrintAB( ) const ;打击哦对Print的有效重载。
c)疑问:常成员函数Test在类中充当什么角色?
d)结果预测:
调用函数CSample::PrintAB()
A=11 B=22
调用函数CSample::PrintAB() const !
A=33 B=44
f)程序在Visual C++上的运行经过是:
2)调试程序2
# include <iostream.h>
class CIncrement
{
int count ;
const int increment ;
public :
CIncrement (int c=0 , int i =1 ) ;
void addIncrement () {count+=increment; } void Print ( ) const ;
} ;
CIncrement ::CIncrement (int c , int i): increment ( i ) { count=c ;}
void CIncrement ::Print ( ) const
{//increment =2 ; cout<<"count="<<count<<"\tincrement="<<i ncrement<<'\n' ;
}
void main ( )
{
CIncrement value (10 ,5);
cout<<"Before incrementing:" ;
value.Print() ;
for (int j=1 ; j<=3 ;j++ )
{ value.addIncrement ( ) ;
cout<<"After increment"<<j<<" :" ; value.Print ( ) ;
}
}
a)类图和对象图的分析如下:
c)功能分析:increment被定义成常成员数据,构造函数通过初始化列表对它
初始化后,任何函数不能对它赋值,所以它的值一直为5,而incrermenting 则随for循环被addIncrement ()函数3次加5.
d)结果预测:Before incrementing count=10 ,increment=5
After increment count=15 ,increment=5
After increment count=20 ,increment=5
After increment count=25 ,increment=5
f)调试结果如下,和预测一致:
3)调试程序3
# include <iostream.h>
class A2 ;
class A1{
int a , b ;
public :
A1(int i , int j){a=i ; b=j ;}
int Geta(){return a ;}
int Getb(){return b;}
void Change (A2 &) ; //A } ;
class A2 {
int c , d ;
public :
A2(int i ,int j) {c=i , d=j ;}
int Getc(){return c;}
int Getd(){return d ;}
friend void A1::Change ( A2 &) ; } ; void A1::Change(A2 &s1)
{a=s1.c ; b=s1.d ;}
void main ( )
{
A1 a1 (1 ,2) ;
A2 a2 (3 ,4) ;
cout<<"a1.a="<<a1.Geta()<<'\t'<<"a1.b="< <a1.Getb()<<endl ;
cout<<"a2.c="<<a2.Getc()<<'\t'<<"a1.c="< <a2.Getd()<<endl ;
a1.Change(a2) ;
cout<<"a1.a="<<a1.Geta()<<'\t'<<"a1.b="< <a1.Getb()<<endl ;
}
a)类图和对象图分析如下:
b) 功能分析:友元函数Change 通过对象名直接访问类的私有c,d 。
c) 结果预测:a1.a=1 a1.b=2
a1.a=3 a1.2=4
a1.a=3 a1.b=4
d) 调试结果:
4)调试程序4
# include<iostream.h>
class CStack ; class CNode { int data ; CNode *prev ; public :
CNode (int d , CNode *n) { data=d ; prev=n ;}
friend class CStack ; };
class CStack { CNode *top ; public:
CStack ( ) {top=0 ;}
void push (int i ) ; int pop ( ) ; };
void CStack ::push (int i ) {
CNode *n=new CNode (i , top) ; top = n ;
}
int CStack ::pop() {
CNode *t=top ; if ( top ){
top=top->prev ; //top=t->prev ;
int c=t->data ; delete t ; return c ; }
return 0 ; }
main ( ) {
int c ; CStack s ;
cout<<"请向栈内输入10个数:" ; for ( int i=0 ; i<10 ; i++) {
cin>>c ; s.push ( c) ;
}
cout<<"依次弹出栈内中的整数:" ; for (i=0 ; i<10 ; i++ ) //将栈中的数依次弹出 cout<<s.pop()<<" " ;
cout<<endl ; return 1 ; }
a) 类图之间的关系如下
b) 功能分析如下:友元类CStack 类的所有成员函数都自动成为CNode 类的友
元函数,都可以访问CNode 类私有和保护成员,从而模拟栈的工作方式编程。
c) 调试结果如下:
4.总结分析
1)预习:通过预习巩固了一下友元和const的基本知识点,同
时对关键知识有个总结归纳,对后来的程序分析和设计起到很大的作用。
2)程序调试情况
首先,用逻辑推理的方法来确定错误语句。
编程序的代码还不算太多,可以逐行逐句的搜查语句的错误之处,尽量避免编译程序的时候出现太多的错误。
参照课本例题,在模仿过程中最大的收获是对友元和const的应用有了进一步的认识。
接着,纠正在编译出现的错误,根据需要修改甚至重新设计。
修改和纠正是比较艰苦繁琐的工作,然后一步一步的运行程序,看是否符合程序原本的设计要求。
3)在实验中感想,
对C++语言的关于友元和const的掌握在实验前以为还可以的,谁知道坐起来就不行。
特别的分析设计的时候,得把它们的知识点弄明白才可以开始,由于平时实践得少,对知识点的掌握不透切,导致编写程序困难重重。
最后不得不从新拿起C++语言的教材来学习例子,通过例子了解友元和const的具体用法。