怎样使用类和对象 1.构造函数和析构函数的作用是什么?什么时候需要自己定义构造函数和析构 函数? 【解】 略。 2.分析下面的程序,写出其运行时的输出结果。 #include<iostream.h> using namespace std; class Date {public: Date(int,int,int); Date(int,int); Date(int); Date(); void display(); private: int month; int day; int year; }; Date::Date(int m,int d,int y):month(m),day(d),year(y) {} Date::Date(int m,int d):month(m),day(d) {year=2005;} Date::Date(int m):month(m) {day=1; year=2005; } Date::Date() {month=1; day=1; year=2005; } void Date::display() {cout<<month<<"/"<<day<<"/"<<year<<endl;} int main() { Date d1(10,13,2005); Date d2(12,30); Date d3(10); Date d4; d1.display(); d2.display(); d3.display(); d4.display(); return 0; } 【解】 程序运行结果为 10/13/2005 12/30/2005 10/1/2005 1/1/2005 3.如果将第2题中程序的第5行改为用默认参数,即 Date(int=1,int=1,int=2005); 分析程序有无问题。上机编译,分析出错信息,修改程序使之能通过编译。要求保留上面一行给出的构造函数,同时能输出与第2题程序相同的输出结果。 【解】 编译时出错,因为构造函数使用默认参数后就不能再使用重载的构造函数,否则就会有歧义,例如在处理 Date d2(12,30); 时,系统无法辨别是调用默认参数的构造函数 Date(int=1,int=1,int=2005); 还是调用重载的构造函数 Date(int,int); 系统不允许出现这样的矛盾现象,会给出出错信息,要求修改程序。可修改程序如下: #include<iostream> using namespace std; class Date {public: Date(int=1,int=1,int=2005); void display(); private: int month; int day; int year; }; Date::Date(int m,int d,int y):month(m),day(d),year(y) {} void Date::display() {cout<<month<<"/"<<day<<"/"<<year<<endl;} int main() { Date d1(10,13,2005); Date d2(12,30); Date d3(10); Date d4; d1.display(); d2.display(); d3.display(); d4.display(); return 0; } 删除重载的构造函数,这时再编译,无错误,运行结果同第2题。 4.建立一个对象数组,内放5个学生的数据(学号、成绩),用指针指向数组首元素,输出第1,3,5个学生的数据。 【解】 程序如下: #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void display(); private: int num; float score; }; void Student::display() {cout<<num<<" "<<score<<endl;} int main() {Student stud[5]={Student(101,78.5),Student(102,85.5),Student(103,98.5), ?? Student(104,100.0), Student(105,95.5)}; Student *p=stud; for(int i=0;i<=2;p=p+2,i++) p->display(); return 0; } 运行时的输出如下: 101 78.5 103 98,5 105 95.5 5.建立一个对象数组,内放5个学生的数据(学号、成绩),设立一个函数max,用指向对象的指针作函数参数,在max函数中找出5个学生中成绩最高者,并输出其 学号。 【解】 程序如下: #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} int num; float score; }; int main() {Student stud[5]={ Student(101,78.5),Student(102,85.5),Student(103,98.5), Student(104,100.0),Student(105,95.5)}; void max(Student* ); Student *p=&stud[0]; max(p); return 0; } void max(Student *arr) {float max_score=arr[0].score; int k=0; for(int i=1;i<5;i++) if(arr[i].score>max_score) {max_score=arr[i].score;k=i;} cout<<arr[k].num<<" "<<max_score<<endl; } 6.阅读下面程序,分析其执行过程,写出输出结果。 #include<iostream.h> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) {num=n;score=s;} void display() {cout<<num<<" "<<score<<endl;} private: int num; float score; }; int main() {Student stud(101,78.5); stud.display(); stud.change(101,80.5); stud.display(); return 0; } 【解】 函数stud.display的作用是输出对象stud中数据成员num和score的值,函数stud.change的作用是改变对象stud中数据成员num和score的值,在调用此函数时给出实参101和80.5,取代了数据成员num和score原有的值。 程序运行结果如下: 101 78.5 (num和score的原值) 101 80.5 (num和score的新值) 7. 将第6题的程序分别进行以下修改,分析修改部分的含义以及编译和运行的情况。 (1)将main函数中的第2行改为 const Student stud(101,78.5); (2)在(1)的基础上修改程序,使之能正常运行,用change函数修改数据成员num和score的值。 (3)将main函数改为 int main() {Student stud(101,78.5); Student *p=&stud; p–>display(); p–>change(101,80.5); p–>display(); return 0; } 其他部分仍同第6题的程序。 (4)在(3)的基础上将main函数第3行改为 const Student *p=&stud; (5)再把main函数第3行改为 Student *const p=&stud; 【解】 (1)有两个错误: ① stud被声明为常对象后,不能调用对象中的一般成员函数(除非把该成员函数也声明为const型),因此在main函数中调用stud.display( )和stud.change( )是非法的。 ② 若将对象stud声明为常对象,其值是不可改变的,而在主程序中,企图用stud.change函数去改变stud中数据成员的值,是非法的。 因此程序在编译时出错。如果将程序第7行改为 void display() const {cout<<num<<" "<<score<<endl;} 把display函数改为const型,可以正常调用display函数。如果把第6行也改为 void change(int n,float s) const {num=n;score=s;} 程序编译时仍然出错,这是由于change函数试图改变stud中数据成员的值。如果删除main函数中调用change函数的一行(可把它改为注释行),则程序能通过编译,可以正常运行。读者可以自己上机调试一下。 (2)要求用change函数修改数据成员num和score的值,则将数据成员num和 score声明为可变的(mutable)数据成员即可。程序如下: #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) const {num=n;score=s;} //常成员函数 void display()const {cout<<num<<" "<<score<<endl;} //常成员函数 private: mutable int num; //用mutable声明可变的数据成员 mutable float score; //用mutable声明可变的数据成员 }; int main() {const Student stud(101,78.5); //常对象 stud.display(); //调用常成员函数 stud.change(101,80.5); //调用常成员函数,修改数据成员 stud.display(); return 0; } 运行结果如下: 101 78.5 (修改前的数据) 101 80.5 (修改后的数据) (3)根据题目要求,程序改为 #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) {num=n;score=s;} void display() {cout<<num<<" "<<score<<endl;} private: int num; float score; }; int main() {Student stud(101,78.5); Student *p=&stud; p–>display(); p–>change(101,80.5); p–>display(); return 0; } 在主函数中定义了指针对象p,它指向stud,函数p–>display(?)相当于stud.display(?)。程序合法,运行结果与第6题程序的运行结果相同。 (4)在(3)的基础上将main函数第3行改为 const Student *p=&stud; 程序如下: #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) {num=n;score=s;} void display() {cout<<num<<" "<<score<<endl;} private: int num; float score; }; int main() {Student stud(101,78.5); const Student *p=&stud; p–>display(); p–>change(101,80.5); p–>display(); return 0; } 在主函数中定义了指向const对象的指针变量p,则其指向的对象的值是不能通过指针变量p改变的。为了安全,C++也不允许通过指针变量p调用对象stud中的非const成员函数,在main函数中调用p–>display(?)和p–>change(?)是非法的。为了能正确调用stud中的display函数,应将程序第7行改为 void display() const {cout<<num<<" "<<score<<endl;} 即将display函数声明为const型。这样保证display函数只能引用而不能修改类中的数据成员。 此外,p–>change(?)试图通过指针变量p修改类中的数据成员的值,这与指向const对象的指针变量的性质不相容,编译时出错。 在上面的基础上,将程序改为 #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) {num=n;score=s;} void display() const{cout<<num<<" "<<score<<endl;} //此行加了const private: int num; float score; }; int main() {Student stud(101,78.5); const Student *p=&stud; p–>display(); stud.change(101,80.5); //注意此行修改了 p–>display(); return 0; } 在main函数中,不通过指针变量p修改数据成员,而直接通过对象名stud调用change函数,则是允许的,编译能通过。因为并未定义stud为常对象,只是定义了p是指向const对象的指针变量,不能通过指针变量p修改类中的数据成员的值,而不通过指针变量p修改类中的数据成员的值是可以的。 同样,如果不是通过指针变量p调用display函数(即“p->display( );”),而是通过对象名stud调用display函数,则不必将display函数声明为const型。 (5)再把main函数第3行改为 Student *const p=&stud; 定义了一个指向对象的常指针,要求指针变量p的指向不能改变,只能始终指向对象stud。今在程序中未改变p的指向,因此程序合法,而且不需要在定义display和change函数时将它们声明为const型。程序能通过编译,并正常运行。运行的结果与第6题的程序的运行结果相同。 8.修改第6题的程序,增加一个fun函数,改写main函数。在main函数中调用fun函数,在fun函数中调用change和display函数。在fun函数中使用对象的引用(Student &)作为形参。 【解】 可以编写以下程序: #include<iostream> using namespace std; class Student {public: Student(int n,float s):num(n),score(s){} void change(int n,float s) {num=n;score=s;} void display() {cout<<num<<" "<<score<<endl;} private: int num; float score; }; int main() {Student stud(101,78.5); void fun(Student &); //声明fun函数 fun(stud); //调用fun函数,实参为对象stud return 0; } void fun(Student &stu) //定义fun函数,形参为Student类对象的引用 {stu.display(); //在fun函数中调用change和display函数 stu.change(101,80.5); stu.display(); } 运行结果如下: 101 78.5 101 80.5 9.商店销售某一商品,商店每天公布统一的折扣(discount)。同时允许销售人员在销售时灵活掌握售价(price),在此基础上,对一次购10件以上者,还可以享受9.8折优惠。现已知当天3个销货员的销售情况为: 销货员号(num) 销货件数(quantity) 销货单价(price) 101 5 23.5 102 12 24.56 103 100 21.5 请编程序,计算出当日此商品的总销售款sum以及每件商品的平均售价。要求用静态数据成员和静态成员函数。 提示:将折扣discount、总销售款sum和商品销售总件数n声明为静态数据成员,再定义静态成员函数average(求平均售价)和display(输出结果)。 【解】 可以编写出以下程序: #include<iostream> using namespace std; class Product {public: Product(int m,int q,float p):num(m),quantity(q),price(p){}; void total(); static float average(); static void display(); private: int num; //销货员号 int quantity; //销货件数 float price; //销货单价 static float discount; //商店统一折扣 static float sum; //总销售款 static int n; //商品销售总件数 }; void Product::total() //求总销售款和销售件数 {float rate=1.0; if(quantity>10) rate=0.98*rate; sum=sum+quantity*price*rate*(1–discount); //累计销售款 n=n+quantity; //累计销售件数 } void Product::display() //输出商品销售总件数和平均售价 {cout<<sum<<endl; cout<<average()<<endl; } float Product::average() //求平均价 {return(sum/n);} float Product::discount=0.05; //对静态数据成员初始化 float Product::sum=0; //对静态数据成员初始化 int Product::n=0; //对静态数据成员初始化 int main() {Product Prod[3]={Product(101,5,23.5),Product(102,12,24.56), Product(103,100,21.5)}; //定义Product类对象数组,并给出数据 for(int i=0;i<3;i++) //统计3个销货员的销货情况 Prod[i].total(); Product::display(); //输出结果 return 0; } 运行结果如下: 2387.66 (总销售款) 20.4073 (平均售价) 读者可以在此基础上对输出结果做一些加工和修饰,如加上必要的文字说明,对输出的数值取两位小数等。 10.将《C++面向对象程序设计(第4版)》例3.13程序中的display函数不放在Time类中,而作为类外的普通函数,然后分别在Time和Date类中将display声明为友元函 数。在主函数中调用display函数,display函数分别引用Time和Date两个类的对象的私有数据,输出年、月、日和时、分、秒。请读者自己完成并上机调试。 【解】 可以编写以下程序: #include<iostream> using namespace std; class Date; //对Date的声明,它是对Date的预引用 class Time {public: Time(int,int,int); friend void display(const Date &,const Time &); //将普通函数display声明为朋友 private: int hour; int minute; int sec; }; Time::Time(int h,int m,int s) {hour=h; minute=m; sec=s; } class Date {public: Date(int,int,int); friend void display(const Date &,const Time &); //将普通函数display声明为朋友 private: int month; int day; int year; }; Date::Date(int m,int d,int y) {month=m; day=d; year=y; } void display(const Date &d,const Time &t) //是Time和Date两个类的朋友 { cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl; //引用Date类对象t1中的数据成员 cout<<t.hour<<":"<<t.minute<<":"<<t.sec<<endl; //引用Time类对象t1中的数据成员 } int main() { Time t1(10,13,56); //定义Time类对象t1 Date d1(12,25,2004); //定义Date类对象d1 display(d1,t1); //调用display函数,用对象名作实参 return 0; } 运行结果如下: 12/25/2004 10:13:56 11.将《C++面向对象程序设计(第4版)》例3.13中的Time类声明为Date类的友元类,通过Time类中的display函数引用Date类对象的私有数据,输出年、月、日和时、分、秒。 【解】 可以编写以下程序: #include<iostream> using namespace std; class Time; class Date {public: Date(int,int,int); friend Time; //将Time类声明为朋友类 private: int month; int day; int year; }; Date::Date(int m,int d,int y):month(m),day(d),year(y){} class Time {public: Time(int,int,int); void display(const Date &); private: int hour; int minute; int sec; }; Time::Time(int h,int m,int s):hour(h),minute(m),sec(s){} void Time::display(const Date &d) { cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl; //引用Date类对象d1的数据成员 cout<<hour<<":"<<minute<<":"<<sec<<endl; //引用Time类对象d1的数据成员 } int main() { Time t1(10,13,56); Date d1(12,25,2004); t1.display(d1); return 0; } 运行结果如下: 12/25/2004 10:13:56 由于Time类是Date类的友元类,因此Time类中的成员函数都是Date类的友元函数,它既可以引用Time类对象的数据成员,又可以引用Date类对象的数据成员。在引用本类(Time类)的数据成员时,不必在数据成员名前面加对象名,而在引用Date类的数据成员时必须在数据成员名前面加上对象名(如d.month,d是形参名,实参是对象d1,因此d.month相当于d1.month)。 12.将《C++面向对象程序设计(第4版)》中的例3.14改写为在类模板外定义各成员函数。 【解】 改写后的程序如下: #include<iostream> using namespace std; template<class numtype> class Compare {public: Compare(numtype a,numtype b); numtype max(); numtype min(); private: numtype x,y; }; //在类模板外定义各成员函数 template<class numtype> Compare<numtype>::Compare(numtype a,numtype b) {x=a;y=b;} template<class numtype> numtype Compare<numtype>::max() {return(x>y)?x:y;} template<class numtype> numtype Compare<numtype>::min() {return(x<y)?x:y;} //主函数 int main() {Compare<int> cmp1(3,7); cout<<cmp1.max()<<"is the Maximum of two integer numbers."<<endl; cout<<cmp1.min()<<"is the Minimum of two integer numbers."<<endl <<endl; Compare<float> cmp2(45.78,93.6); cout<<cmp2.max()<<"is the Maximum of two float numbers."<<endl; cout<<cmp2.min()<<"is the Minimum of two float numbers."<<endl<<endl; Compare<char>cmp3('a','A' ); cout<<cmp3.max()<<"is the Maximum of two characters."<<endl; cout<<cmp3.min()<<"is the Minimum of two characters."<<endl; return 0; } 运行结果为 7 is the Maximum of two integers. 3 is the Minimum of two integers. 93.6 is the Maximum of two float numbers. 45.78 is the Minimum of two float numbers. a is the Maximum of two characters. A is the Minimum of two characters. 28 C++面向对象程序设计(第4版)学习辅导 29 第3章 怎样使用类和对象