面向对象程序设计实验报告
学生所在学院:信息科学与工程学院 学生所在班级: 学生姓名: 指导教师:
实验一 C++基础
1.1实验目的
1.了解并熟悉开发环境,学会调试程序;
2.熟悉C++中简单的标准输入输出函数的使用方法; 3.理解const修饰符的作用并学会应用;
4.理解内联函数的优缺点并学会其使用场合; 5.理解并学会函数重载;
6.理解并熟练掌握使用new和delete来分配内存; 7.理解并熟练掌握引用的使用方法。 1.2实验内容 1.2.1程序阅读
1.理解下面的程序并运行,然后回答问题。 #include
int max_def(int x,int y,int z) { int temp=0; return(temp=(x>y?x:y))>z?temp:z; }
double max_def(double x,double y) { return(x>y?x:y); }
int main() { int x1=0; int x2=0; double d1=0.0; double d2=0.0; x1=max_def(5,6); x2=max_def(2,3,4); d1=max_def(2.1,5.6); d2=max_def(12.3,3.4,7.8);-------------------------------------------------------① cout<<\ cout<<\ cout<<\ cout<<\②
return 1; }
问题一:上述程序的输出结果是什么? 答:输出结果为
问题二:①处调用的是哪个函数?
答:①处调用的是double max_def(double x,double y)函数 问题三:②处的输出结果为什么是d2=12,而不是d2=12.3? 答:因为调用的int max_def(int x,int y,int z)函数返回值为整型。 2.理解下面的程序并运行,然后回答问题。 #include
问题一:①、②、③处动态申请内存分别代表什么意思?
答:①处:定义一个整型指针动态分配一个整型空间将首地址赋给p1;
②处:定义一个整型指针动态分配一个整型空间,并赋值为0;
③处:定义一个字符型指针动态分配具有10个数组元素的字符数组空间,将首地址赋给p3。
问题二:该程序存在什么不合理的地方? 答:程序结束时没有释放分配的存储空间。 3. 理解下面的程序并运行,然后回答问题。 #include
void swap(int a,int b) { int temp=a; a=b; b=temp; }
void swap(int*a,int*b) { int temp=*a; *a=*b; *b=temp; }
int main() { int i=5; int j=10; cout<<\ swap(i,j); cout<<\ swap(&i,&j); cout<<\ return 1; }
问题一:输出结果是什么? 答
:
问题二:①处函数调用不能实现两个数的交换,而②可以,原因是什么? 答:①处调用的函数形参为整型变量,是值传递,形参的改变不影响实参; ②处调用的函数形参为指针,是址传递,形参的改变会导致实参的改变。 问题三:②处调用的是哪个函数?
答:调用的函数是void swap(int*a,int*b)。
实验二 类和对象——类的构建
2.1 实验目的 1.类的定义; 2.类对象的使用;
3.类成员变量的定义和使用; 4.类成员函数的定义和使用; 5.理解类的作用域; 6.理解类的声明;
7.理解类中成员的访问属性; 2.2 实验内容 2.2.1程序阅读
1. 理解下面的程序并运行,然后回答后面的问题。 #include
public: void Set(int m,int d,int y) { month=m; day=d; year=y; } int IsLeapYear() { return (year%4==0 && year0!=0)||(year@0==0); } void Print() { cout< }; void main() { CDate a; a.Set(10,15,2007); a.Print(); } 问题一:以上程序为什么不能通过编译?如何改正? 答:因为Set函数被设为私有main函数中无法调用,应在set函数前加public。 问题二:类中的成员函数和成员变量分别属于哪种访问权限? 答:数据成员为私有,Print为公有;set原为私有,改正后为公有。 问题三:①处语句的意思是什么? 答:将类定义一个对象a。 2.理解下面的程序并运行,然后回答问题。 . #include void Set(int m,int d,int y) { ::month=m; ::day=d; ::year=y; } class CDate { public: void Set(int m,int d,int y) { ::Set(m,d,y); } void Print() { cout< int day; int year; }; void main() { CDate a; a.Set(10,15,2007); a.Print(); } 问题一:①处是在给成员变量赋值还是全局变量赋值,如果去掉域作用符::,程序是否还能正确运行? 答:全局变量,不能。 问题二:②处调用的哪个函数,如果去掉域作用符::,程序是否还能正确运行? 答:调用全局set函数;不能。 问题三:程序的输出结果是? 答: 实验三 类和对象——构造函数与析构函数 3.1实验目的 1.理解this指针的作用和用法; 2.掌握构造函数的定义和作用; 3.掌握构造函数的使用; 4.掌握拷贝构造函数的定义和使用; 5.掌握构造函数的重载; 6.掌握析构函数的定义和使用。 3.2实验内容 3.2.1程序阅读 1.理解下面的程序并运行,然后回答问题。 #include public: void Set(int x,int y); void Print(); private: int x; int y; }; void CPoint::Set(int x,int y) { x=x; y=y; } void CPoint::Print() { cout<<\} void main() { CPoint pt; pt.Set(10,20); pt.Print(); } 问题一:以上程序编译能通过吗?如果不能,原因是什么? 答:能通过编译。 问题二:以上程序的运行结构是否正确,如果不正确,分析为什么,如何改正? 答:运行结构不正确,因为Set函数中,x=x,参数x覆盖数据成员,使得数据成员并没有被赋值。 2.理解下面的程序并运行,然后回答后面的问题。 include public: void Print(); private: CPerson(); private: int age; char*name; }; CPerson::CPerson() { } void CPerson::Print() { cout<<\} void main() { CPerson ps(23,\张三\ ps.Print(); } 问题一:以上程序存在三个错误,在不改变主函数内容的前提下,试改正该程序。 答:第一处错误是:在程序头应添加#include #include public: void Print(); CPerson(int i ,char *j); public: int age; char *name; }; CPerson::CPerson(int i,char *j) { age=i; name=j; } void CPerson::Print() { cout<<\} void main() { CPerson ps(23,\张三\ ps.Print(); } 实验四 类和对象—对象传递与静态成员 4.1 实验目的 1. 静态成员(静态数据成员、静态成员函数)的作用与使用时的注意事项 2.掌友元(友元函数、友元类)的作用和使用; 3. 理解常类型。 4.2 实验内容 4.2.1 程序阅读 1. 理解下面的程序并运行,然后回答后面的问题。 #include public: CStudent(char*n,int a); ~CStudent(); static void SetAge(int age); private: char*name; int age; static int nTotalObj; }; int CStudent::nTotalObj=0; CStudent::CStudent(char*n,int a) :age(a) { int nLcn=strlen(n); name=new char[nLen+1]; strcpy(name,n); name[nLen]='\\0'; nTotalObj++; } CStudent::~CStudent() { delete[]name; nTotalObj--; } void CStudent::SetAge(int age) { this->age=age; } void main() { CStudent stu1(\张三\ CStudent str2(\李四\ cout<<\} 问题一:以上程序编译能通过吗,为什么? 答:不能,CStudent::nTotalObj为私有变量,类引不能访问。 问题二:成员变量nTotalObj起什么作用,它是如何实现的? 答:统计该类所创建对象的个数,通过声明为静态。 问题三:如果不改编主函数和类Cstudent中的成员变量的属性,应该如何改正该程序? 答:可以将static int nTotalObj声明为公有。 2. 理解下面的程序并运行,然后回答后面的问题。 #include public: CStudent(char*n,int a); ~CStudent(); private: char*name; int age; }; CStudent::CStudent(char*n,int a) :age(a) { int nLen=strlen(n); name=new char[nLen+1]; strcpy(name,n); name[nLen]='\\0'; } CStudent::~CStudent() { delete[]name; } class CTeacher { public: CTeacher(char*tn,int ta); ~CTeacher(); void SetStuAge(int a); private: char*name; int age; CStudent stu; }; CTeacher::CTeacher(char*tn,int ta) :age(ta) { int nLen=strlen(tn); name=new char[nLen+1]; strcpy(name,tn); name[nLen]='\\0'; } CTeacher::~CTeacher() { delete[]name; } void CTeacher::SetStuAge(int a) { stu.age=a; } void main() { CStudent stu1(\张三\ CStudent str2(\李四\} 问题一:以上程序有两个错误,指出并改正。 答:第一处错误是Cstudent age为私有,Cteacher无法访问。 第二处错误是Cteacher中Cstudent未初始化。 改正:①public:int age②Cteacher::Cteacher(char*tn,int ta)::age(ta),stu(tn,ta) 3. 理解下面的程序并运行,然后回答后面的问题。 #include private: const int year; const int month; const int day; public: Date(int y,int m,int d); void showdate(); }; Date::Date(int y,int m,int d) { year=y; month=m; day=d; } void Date::showdate() { cout< void main() { const Date obj(2007,10,30); obj.showdate(); } 问题一:以上程序有两个错误,试指出来,并改正之? 答:第一处错误是:构造函数应该用成员初始化列表对常数据成员初始化;第二处错误是:没有用常成员函数访问常对象数据成员。改正后的程序为: #include private: const int year; const int month; const int day; public: Date(int y,int m,int d); void showdate()const ; }; Date::Date(int y,int m,int d):year(y),month(m),day(d) { } void Date::showdate()const { cout< void main() { Date obj(2007,10,30); obj.showdate(); } 实验五 派生与继承—单基派生 5.1 实验目的 1. 理解继承的概念; 2.理解共有派生、私有派生和保护派生; 3. 理解单基派生类中构造函数和析构函数的执行顺序。 5.2 实验内容 5.2.1 程序阅读 1. 理解下面的程序并运行,然后回答后面的问题。 #include public: CBase(int a) :a(a) { } protected: void print() { cout<<\ } private: int a; }; class CDerive:public CBase { public: void print() { CBase::print(); cout<<\ } private: int b; } void main() { CDerive d; d.print(); CBase b; b.print(); } 问题一:以上程序有两个错误,试指出来,并改正之。 答:类CBase中的成员数据应该为公有访问属性,第二个错误是构造函数有问题。改正后的程序有两种: ① #include Void print() { cout<<\ } public: int a; }; class CDerive:public CBase { public: CDerive(int a,int b):CBase(a),b(b) { } void print() { CBase::print(); cout<<\} private: int b; }; void main() { CDerive d(6,4); d.print(); CBase b(5); b.print(); } ② #include class CDerive:public CBase { public: void print() { CBase::print(); cout<<\ } private: int b; }; void main() { CDerive d; d.print(); CBase b; b.print(); } 2. 理解下面的程序并运行,然后回答后面的问题。 #include\class CBase { public: CBase(int a) :a(a) { cout<<\ } ~CBase() { cout<<\ } void print() { cout<<\ } protected: int a; }; class CDerive:public CBase { public: CDerive(int a,int b,int c) :CBase(a),b(b),c(c) { cout<<\ } ~CDerive() { cout<<\ } void print() { CBase::print(); cout<<\ cout<<\ } private: CBase b; int c; }; void main() { CDerive d(1,2,3); d.print(); } 问题一:以上程序的输出结果是什么,为什么? 答:输出结果是 原因是 基类和派生类中构造函数和析构函数的执行顺序。 问题二:①处语句执行完后,d.b.a的值为多少? 答:值为1。 实验六 派生与继承—多基派生 6.1 实验目的 1. 理解多基派生的定义; 2. 基派生中的构造函数与析构函数的调用顺序; 3. 理解多基派生中虚基类的作用。 6.2实验内容 6.2.1 程序阅读 1. 理解下面的程序并运行,然后回答后面的问题。 class CBase1 { public: CBase1(int a) :a(a) { cout<<\ } ~CBase1() { cout<<\ } void print() { cout<<\ } protected: int a; }; class CBase2 { public: CBase2(int b) :b(b) { cout<<\ } ~CBase2() { cout<<\ } void print() { cout<<\ } protected: int b; }; class CDerive:public CBase1,public CBase2 { public: CDerive() { cout<<\ } ~CDerive() { cout<<\ } void print() { CBase1::print(); CBase2::print(); b1.print(); b2.print(); cout<<\ } private: CBase1 b1; CBase2 b2; int c; }; void main() { CDerive d; d.print(); } 问题一:改正以上程序中的错误,并分析输出结果。 答:CBase1与CBase2没有合适的构造函数,改正方法在CBase1,CBase2设置缺省值为0 改正:将CDerive构造函数改为:CDerive(int a,int b,int c): CBase1(a),CBase2(b),b1(a),b2(b),c(c) { cout<<\ } 主函数中CDerive d;改为 CDerive d (1,2,3); 输出结果是: 实验七 多态性—函数与运算符的重载 7.1 实验目的 1. 理解静态联编和动态联编的概念; 2. 掌握成员函数方式运算符重载; 3. 掌握友元函数方式运算符重载; 4. 掌握++、--、=运算符的重载。 7.2 实验内容 1. 理解下面的程序并运行,然后回答后面的问题。 #include\class CComplex { public: CComplex() { real=0; imag=0; } CComplex(int x,int y) { real=x; imag-y; } int real; int imag; CComplex operator+(CComplex obj1) { CComplex obj2(real+obj1.real,imag+obj1.imag); return obj2; } }; void main() { CComplex obj1(100,30); CComplex obj2(20,30); CComplex obj; obj=obj1+obj2; cout< 问题一:①处的运算符重载,为什么该函数的返回值要设计成Ccomplex类型? 答:①处运算符重载,要返回两个值real和imag,所以函数返回值类型设计为CComplex类型。 问题二:②处的运算符重载函数调用就相当于“obj=operator+(obj1,obj2);”,请问CComplex类中的运算符重载函数为什么只有一个参数? 答:因为调用重载运算符的对象本身相当于一个参数。 课后习题 第二章 一 实验目的 1.掌握引用的使用。 2.掌握调用函数的方法。 二.实验内容 2.19 写出下列程序的运行结果。 #include int temp; temp=m; m=n; n=temp; } Int main() { int a=5,b=10; f(a,b) cout< } 三.实验结果 四.心得体会 1.引用可以作为函数形参,和指针变量作为函数参数效果一样,但是引用作为函数参数更清晰。 2. 第三章 一.实验目的 1.掌握静态数据成员的使用。 2.掌握静态成员函数的使用。 二.实验内容 3.26 写出下列程序的运行结果。 #include static int B; public: M(int a) { A=a; B+=a; cout<<”Constructing\\n”< Static void f1(M m); ~M() { cout<<”Destructing\\n”< Void M::f1(M m) { Cout<<”A=”< int M::B=0; int main() { M P(5),Q(10); M::f1(P); M::f1(Q); Return 0; } 三.实验结果 四.心得体会 1.静态数据成员主要用作类的所有对象所公用的数据,它在类的任何对象建立前就存在了。 2.一般情况下,静态成员函数来访问静态数据成员。 第四章 一.实验目的 1.掌握派生类的继承方式。 2.掌握派生类构造函数和析构函数。 3.掌握派生类构造函数和析构函数的执行顺序。 二.实验内容 4.15 写出下列程序的运行结果。 #include cout<<”constructor B1.”< Void print() { cout< class B2{ int b2; public: B2(int i) { B2=i; cout<<”constructor B2.”< Void print() { cout< class B3{ int b3; public: B3(int i) { B3=i; cout<<”constructor B3.”< int getb3() { return b3; } }; class A :public B2,public B1{ int a; B3 bb; public: A(int i,int j,int k,int l):B1(i),B2(j),bb(k) {a=1; cout<<”constructor A.”< void print() { B1::print(); B2::print(); cout< int main() { A aa(1,2,3,4); aa.print(); return 0; } 三.实验结果 四.心得体会 1.派生类继承了基类的成员,实现了原有代码的重用,但是基类的构造函数和析构函数不能被继承,对所有从基类继承下来的成员初始化工作还是由基类构造函数完成,但是我们必须在派生类中对基类的构造函数所需要的参数进行设置。 2.构造函数的执行顺序严格地按照先调用基类的构造函数后调用派生类的构造函数。 第五章 一.实验目的 1.掌握虚函数的使用。 二.实验内容 5.13 下面的程序段中虚函数被重新定义的方法正确吗?为什么? class base { Public: virtual int f(int a)=0; .... }; class derived:public base { public: int f(int a,int b) { return a*b; } ....... }; 三.实验结果 答:虚函数被重新定义的方法不正确,因为虚函数被重新定义时参数类型和参数个数不能改变。 四.心得体会 虚函数是重载的另一种方式,是动态的,它提供了一种更为灵活的运行时的多态性的机制。