C++第十一章__用类方法合并另个时间(涉及到函数返回值能不能是引用的问题)__运算符重载__友元函数__对<<运算符的重载&友元函数__ cin.clear()的用法__极坐标和直角坐标的相互转换(随机漫步的实现)__将double、int等数据类型赋值给类对象__将类对象赋值给double、int等型的变量(转换函数)
---恢复内容开始---
目录
- 用类方法合并另个时间(涉及到函数返回值能不能是引用的问题)
- 运算符重载
- 友元函数
- 对<<运算符的重载&友元函数
- cin.clear()的用法
- 极坐标和直角坐标的相互转换(随机漫步的实现)
- 将double、int等数据类型赋值给类对象
- 将类对象赋值给double、int等型的变量(转换函数)
用类方法合并另个时间&运算符重载(涉及到函数返回值能不能是引用的问题)
用类方法合并另个时间的代码如下:
1 //mytime.h 2 #ifndef MYTIME_H_ 3 #define MYTIME_H_ 4 5 class Time 6 { 7 private: 8 int hour; 9 int minitue; 10 public: 11 Time(); //声明默认构造函数 12 //Time(int & h, int & m); //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 13 Time(int h, int m); //声明构造函数 14 Time Sum(Time & T); //声明返回值为类对象的函数,形参为指向类对象的引用 15 void Addhour(int h); //单独的增加小时 16 void Addminitue(int m); //单独的增加分钟 17 void Reset(int h=0, int m=0); //重置时间 18 void show(); 19 }; 20 21 #endif
1 //mytime.cpp 2 #include <iostream> 3 #include "mytime.h" 4 5 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 6 { 7 hour = 0; 8 minitue = 0; 9 } 10 11 12 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 13 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 14 //即 int & h = 4; 这样是不合法的 15 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 16 { 17 hour = h; 18 minitue = m; 19 } 20 21 Time Time::Sum(Time & T) 22 { 23 Time s; //新建一个TIme对象,用于作为该函数的返回值 24 s.minitue = minitue + T.minitue; 25 s.hour = hour + T.hour + s.minitue / 60; 26 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 27 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 28 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 29 30 return s; 31 } 32 //注意:Sum()函数的返回值不能是Time & (指向Time对象的引用),这是由于返回的对象时s,而s是一个在Sum() 33 //中定义的局部变量,Sum()函数执行完毕后,s将会消失,返回一个消失的引用是不合适的 34 //所以这里返回s对象的副本,之后在主函数中可以使用它 35 //以前函数的返回值可以为引用,是因为返回的对象均为从主函数中传入的对象,这些对象都是在主函数中定义的 36 //所以可以返回,比如this指针那里,传入一个对象和this指针指向的调用类方法的对象 37 38 //只是增加小时 39 void Time::Addhour(int h) 40 { 41 hour = hour + h; 42 } 43 44 //只是增加分钟 45 void Time::Addminitue(int m) 46 { 47 minitue = minitue + m; 48 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 49 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 50 } 51 52 //重置时间 53 void Time::Reset(int h, int m) 54 { 55 hour = h; 56 minitue = m; 57 } 58 59 //显示对象中的数据 60 void Time::show() 61 { 62 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 63 }
1 //user_main.cpp 2 #include <iostream> 3 #include "mytime.h" 4 5 int main() 6 { 7 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 8 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 9 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 10 11 s1 = s2.Sum(s3); //将Sum()中的this指向s2,s形参用实参s3代替 12 s1.show(); 13 14 s1.Addhour(3); //对s1对象中的数据,只是增加小时 15 s1.show(); 16 17 system("pause"); 18 return 0; 19 } 20 /* 总结 */ 21 /* 22 01)关于引用的使用方法:int & rt = 3; 这样使用是不合法的,要注意在函数参数传递的时候不要发生这样的错误 23 02)关于函数返回值的问题:如果一个变量(包括对象)是在该函数内创建的,那么是不可以以引用的方式返回的 24 因为引用返回的都是该变量本身,而该变量在对应的函数执行完毕之后就消失了,从而发生错误。 25 但是如果变量(对象)是从主函数中传入的,并且返回的也是从主函数中传入的变量(对象),那么是可以以引用 26 的方式返回的。 27 03) 28 */
/* 总结 */
01)关于引用的使用方法:int & rt = 3; 这样使用是不合法的,要注意在函数参数传递的时候不要发生这样的错误
02)关于函数返回值的问题:如果一个变量(包括对象)是在该函数内创建的,那么是不可以以引用的方式返回的
因为引用返回的都是该变量本身,而该变量在对应的函数执行完毕之后就消失了,从而发生错误。
但是如果变量(对象)是从主函数中传入的,并且返回的也是从主函数中传入的变量(对象),那么是可以以引用
的方式返回的。
执行结果:
运算符重载
01)要使用重载运算符,必须使用被称为运算符函数的特殊函数形式:
operaterop(argument-list)
其中operater为关键字,op是要重载的运算符,argument-list为形参,相当于创建一个名字为operaterop的函数
比如对+进行重载即:operater+(),该函数没有形参
op必须是有效的C++运算符,不能是@,但可以是[],因为[]是数组索引运算符
02)运算符重载实际上仍然是函数调用,只不过换了一种形式
假如对+进行重载即:operater+(Time & s) 其中Time是一个类
那么就可以说使用如下方式对两个对象进行相加:
s1 = s2 + s3; //其中s1、s2、s3都是Time类对象
编译器发现s1 s2 s3都是类对象,因此使用相应的运算符函数进行替换:
s1 = s2.operater+(s3); //所以说运算符重载实际上也还是函数调用
03)s1 = s2 + s3;该式隐式的使用s2(因为s2调用了类方法),显式的使用了类对象s3(因为s3作为参数传入)
当然s1 = s3 + s2; 也是可以的,因为s2和s3都是类对象
上句就相当于s1 = s3.operater+(s2);了,即s3调用方法,s2作为参数传入
/* 时间的运算,引入了+运算符重载、-运算符重载和*运算符重载 */
1 //mytime.h 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 #ifndef MYTIME_H_ 5 #define MYTIME_H_ 6 7 class Time 8 { 9 private: 10 int hour; 11 int minitue; 12 public: 13 Time(); //声明默认构造函数 14 Time(int h, int m); //声明构造函数 15 Time operator+(const Time & T) const; //对运算符进行重载,形参为执行类对象的引用,返回值为Time对象, 16 //const Time & T 表明方法operator+()也不能修改作为参数传入的对象中的数据 17 //最后一个const表明operater+()方法不能修改调用这个方法的对象中的数据 18 Time operator-(const Time & T) const; //两个const可有可无 19 Time operator*(double d) const; //最后一个const还是表明不能修改调用operator*()方法的对象中的数据 20 void Addhour(int h); //单独的增加小时 21 void Addminitue(int m); //单独的增加分钟 22 void Reset(int h=0, int m=0); //重置时间 23 void show(); 24 }; 25 26 #endif 27 28 /* 运算符重载 */ 29 /* 30 01)要使用重载运算符,必须使用被称为运算符函数的特殊函数形式: 31 operaterop(argument-list) 32 其中operater为关键字,op是要重载的运算符,argument-list为形参,相当于创建一个名字为operaterop的函数 33 比如对+进行重载即:operater+(),该函数没有形参 34 op必须是有效的C++运算符,不能是@,但可以是[],因为[]是数组索引运算符 35 02)运算符重载实际上仍然是函数调用,只不过换了一种形式 36 假如对+进行重载即:operater+(Time & s) 其中Time是一个类 37 那么就可以说使用如下方式对两个对象进行相加: 38 s1 = s2 + s3; //其中s1、s2、s3都是Time类对象 39 编译器发现s1 s2 s3都是类对象,因此使用相应的运算符函数进行替换: 40 s1 = s2.operater+(s3); //所以说运算符重载实际上也还是函数调用 41 03)s1 = s2 + s3;该式隐式的使用s2(因为s2调用了类方法),显式的使用了类对象s3(因为s3作为参数传入) 42 当然s1 = s3 + s2; 也是可以的,因为s2和s3都是类对象 43 上句就相当于s1 = s3.operater+(s2);了,即s3调用方法,s2作为参数传入 44 45 */
1 //mytime.cpp 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 //重载-运算符Time Time::operator-(const Time & T) const 5 //重载*运算符Time Time::operator*(double d) const 6 #include <iostream> 7 #include "mytime.h" 8 9 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 10 { 11 hour = 0; 12 minitue = 0; 13 } 14 15 16 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 17 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 18 //即 int & h = 4; 这样是不合法的 19 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 20 { 21 hour = h; 22 minitue = m; 23 } 24 25 Time Time::operator+(const Time & T) const 26 { 27 Time s; //新建一个TIme对象,用于作为该函数的返回值 28 s.minitue = minitue + T.minitue; 29 s.hour = hour + T.hour + s.minitue / 60; 30 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 31 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 32 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 33 34 return s; 35 } 36 //注意:Sum()函数的返回值不能是Time & (指向Time对象的引用),这是由于返回的对象时s,而s是一个在Sum() 37 //中定义的局部变量,Sum()函数执行完毕后,s将会消失,返回一个消失的引用是不合适的 38 //所以这里返回s对象的副本,之后在主函数中可以使用它 39 //以前函数的返回值可以为引用,是因为返回的对象均为从主函数中传入的对象,这些对象都是在主函数中定义的 40 //所以可以返回,比如this指针那里,传入一个对象和this指针指向的调用类方法的对象 41 42 //对-运算符进行重载 43 Time Time::operator-(const Time & T) const 44 { 45 Time s; //创建一个局部对象,作为返回值 46 s.minitue = minitue - T.minitue; 47 s.hour = hour - T.hour + s.minitue/60; //虽然是减,但是为了以防万一,还是加上这个取整吧 48 s.minitue = s.minitue % 60; 49 50 return s; 51 } 52 53 //对*运算符进行重载 54 Time Time::operator*(double d) const 55 { 56 Time s; //创建一个局部对象,作为返回值 57 long total_time = hour * 60 * d + minitue * d;//这种都hour和minitue都相乘的方法是从书中学到的 58 s.hour = total_time / 60; 59 s.minitue = total_time % 60; 60 61 return s; 62 } 63 64 //只是增加小时 65 void Time::Addhour(int h) 66 { 67 hour = hour + h; 68 } 69 70 //只是增加分钟 71 void Time::Addminitue(int m) 72 { 73 minitue = minitue + m; 74 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 75 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 76 } 77 78 //重置时间 79 void Time::Reset(int h, int m) 80 { 81 hour = h; 82 minitue = m; 83 } 84 85 //显示对象中的数据 86 void Time::show() 87 { 88 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 89 }
1 //user_main.cpp 2 //使用运算符重载版本 3 //用s1=s2+s3代替s1 = s2.Sum(s3);即可 4 //s1=s2+s3;实际上是调用函数的方法:s1=s2.operator+(s3); 5 #include <iostream> 6 #include "mytime.h" 7 8 int main() 9 { 10 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 11 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 12 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 13 14 std::cout << "使用+重载运算符:" << std::endl; 15 s1 = s2 + s3; //等价于s1=s2.operator+(s3); s2和s3可以互换位置 16 s1.show(); 17 18 std::cout << "使用类方法Addhour():" << std::endl; 19 s1.Addhour(3); //对s1对象中的数据,只是增加小时 20 s1.show(); 21 22 std::cout << "使用-运算符重载方法:" << std::endl; 23 s1 = s1 - s2; //等价于s1=s1.operator-(s2);s1和s2可以互换位置 24 s1.show(); 25 26 std::cout << "使用*运算符重载方法:" << std::endl; 27 s1 = s1 * 2; //等价于s1 = s1.operator-(2); 28 s1.show(); 29 //这里s1只能是在*的左边,2只能是在*的右边 30 //在*的左边的标识符是要调用operator*()方法的,显然数字不能调用该方法 31 //此项缺陷也为以后的友元函数的提出打下了基础 32 33 system("pause"); 34 return 0; 35 } 36 /* 总结 */ 37 /* 38 01)关于引用的使用方法:int & rt = 3; 这样使用是不合法的,要注意在函数参数传递的时候不要发生这样的错误 39 02)关于函数返回值的问题:如果一个变量(包括对象)是在该函数内创建的,那么是不可以以引用的方式返回的 40 因为引用返回的都是该变量本身,而该变量在对应的函数执行完毕之后就消失了,从而发生错误。 41 但是如果变量(对象)是从主函数中传入的,并且返回的也是从主函数中传入的变量(对象),那么是可以以引用 42 的方式返回的。 43 03) 44 */
执行结果为:
友元函数
01)问题的提出:
对于上一个代码中的对*的函数重载中 Time operator*(double d) const;
对Time对象s1和s2,以及一个double值2.1
使用方法只能是s1 = s2*2.1;//实际上是调用对*的重载函数:s1=s2.operator(2.1);
所以s1 = 2.1*s2; 是会报错的
02)解决方法:
A 写注释:告诉每个人只能按照s2*2.1这种方式去写,不能写成2.1*s2
B 使用非成员函数,非成员函数不是由对象调用的,它使用的值都必须是由实参的形式传入的
但是也引发了一个新问题:非成员函数不能访问私有数据。最终的解决方法是使用友元函数
此处引入友元函数的目的是实现乘法的交换律
03)友元函数的声明方法:使用关键字friend
friend Time operator*(doubla m,const Time & t);//该友元函数同时对*运算符进行了重载
该声明意味着下面两点:
A 虽然operator*()是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用; ***
B 虽然operatir*()不是成员函数,但它有成员函数的访问权限。(可以访问私有数据) ***
04)友元函数的定义方法:不用使用关键字friend,因为它不是成员函数,所以也不用使用Time::限定符
Time operator*(double m, Time & t) const
{
Time result;
long total_time = t.hour * m + t.minitue * m;
result.hour = total_time / 60;
result.minitue = total_time % 60;
return result;
}
05)友元函数调用方法:
有了友元函数之后,就可以直接使用 s1 = 2.1*s2;
编译器将s1 = 2.1*s2;转换成s1 = operator*(2.1,s2);友元函数版本
06)稍作修改,就可以将友元函数改变成非友元函数:
//在该非友元函数中调用对*的重载函数
Time operator*(double m, Time & t) const
{
return t*m; //即实际调用的函数为 t.operator*(m),即调用的函数是对*的重载的函数
//原来的版本是显式的访问t.hour和t.minitue.由于这里是将对象t整体使用的,所以t*m将调用对*的重载函数
}
07)总结:
如果在类声明中即声明了对*的重载函数Time operator*(double d) const;
在类声明在又声明了友元函数:friend Time operator*(doubla m,const Time & t);
该友元函数同时对*运算符进行了重载,那么在主函数中就可以使用下面的两种方式
s1 = s2 * 2.1; //调用对*的重载函数,编译器将其转换为s1 = s2.operator(2.1);
s1 = 2.1 * s2; //调用友元函数,编译器将其转换为:s1 = operator(2.1,s2);
即实现了乘法的交换律。
1 //mytime.h 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 #ifndef MYTIME_H_ 5 #define MYTIME_H_ 6 7 class Time 8 { 9 private: 10 int hour; 11 int minitue; 12 public: 13 Time(); //声明默认构造函数 14 Time(int h, int m); //声明构造函数 15 Time operator+(const Time & T) const; 16 Time operator-(const Time & T) const; //两个const可有可无 17 Time operator*(double d) const; //最后一个const还是表明不能修改调用operator*()方法的对象中的数据 18 friend Time operator*(double m, const Time & t); //声明一个友元函数,只允许中声明的时候使用friend关键字 19 void Addhour(int h); //单独的增加小时 20 void Addminitue(int m); //单独的增加分钟 21 void Reset(int h=0, int m=0); //重置时间 22 void show(); 23 }; 24 25 #endif 26 27 28 /* 友元函数 */ 29 /* 30 01)问题的提出: 31 对于上一个代码中的对*的函数重载中 Time operator*(double d) const; 32 对Time对象s1和s2,以及一个double值2.1 33 使用方法只能是s1 = s2*2.1;//实际上是调用对*的重载函数:s1=s2.operator(2.1); 34 所以s1 = 2.1*s2; 是会报错的 35 02)解决方法: 36 A 写注释:告诉每个人只能按照s2*2.1这种方式去写,不能写成2.1*s2 37 B 使用非成员函数,非成员函数不是由对象调用的,它使用的值都必须是由实参的形式传入的 38 但是也引发了一个新问题:非成员函数不能访问私有数据。最终的解决方法是使用友元函数 39 03)友元函数的声明方法:使用关键字friend 40 friend Time operator*(doubla m,const Time & t);//该友元函数同时对*运算符进行了重载 41 该声明意味着下面两点: 42 A 虽然operator*()是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用; 43 B 虽然operatir*()不是成员函数,但它有成员函数的访问权限。(可以访问私有数据) 44 04)友元函数的定义方法:不用使用关键字friend,因为它不是成员函数,所以也不用使用Time::限定符 45 Time operator*(double m, Time & t) const 46 { 47 Time result; 48 long total_time = t.hour * m + t.minitue * m; 49 result.hour = total_time / 60; 50 result.minitue = total_time % 60; 51 return result; 52 } 53 05)友元函数调用方法: 54 有了友元函数之后,就可以直接使用 s1 = 2.1*s2; 55 编译器将s1 = 2.1*s2;转换成s1 = operator*(2.1,s2);友元函数版本 56 06)稍作修改,就可以将友元函数改变成非友元函数: 57 //在该非友元函数中调用对*的重载函数 58 Time operator*(double m, Time & t) const 59 { 60 return t*m; 61 //原来的版本是显式的访问t.hour和t.minitue.由于这里是将对象t整体使用的,所以t*m将调用对*的重载函数 62 //即实际调用的函数为 t.operator*(m) 63 } 64 07)总结: 65 如果在类声明中即声明了对*的重载函数Time operator*(double d) const; 66 在类声明在又声明了友元函数:friend Time operator*(doubla m,const Time & t); 67 该友元函数同时对*运算符进行了重载,那么在主函数中就可以使用下面的两种方式 68 s1 = s2 * 2.1; //调用对*的重载函数,编译器将其转换为s1 = s2.operator(2.1); 69 s1 = 2.1 * s2; //调用友元函数,编译器将其转换为:s1 = operator(2.1,s2); 70 即实现了乘法的交换律。 71 */
1 //mytime.cpp 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 //重载-运算符Time Time::operator-(const Time & T) const 5 //重载*运算符Time Time::operator*(double d) const 6 #include <iostream> 7 #include "mytime.h" 8 9 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 10 { 11 hour = 0; 12 minitue = 0; 13 } 14 15 16 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 17 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 18 //即 int & h = 4; 这样是不合法的 19 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 20 { 21 hour = h; 22 minitue = m; 23 } 24 25 Time Time::operator+(const Time & T) const 26 { 27 Time s; //新建一个TIme对象,用于作为该函数的返回值 28 s.minitue = minitue + T.minitue; 29 s.hour = hour + T.hour + s.minitue / 60; 30 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 31 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 32 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 33 34 return s; 35 } 36 //注意:Sum()函数的返回值不能是Time & (指向Time对象的引用),这是由于返回的对象时s,而s是一个在Sum() 37 //中定义的局部变量,Sum()函数执行完毕后,s将会消失,返回一个消失的引用是不合适的 38 //所以这里返回s对象的副本,之后在主函数中可以使用它 39 //以前函数的返回值可以为引用,是因为返回的对象均为从主函数中传入的对象,这些对象都是在主函数中定义的 40 //所以可以返回,比如this指针那里,传入一个对象和this指针指向的调用类方法的对象 41 42 //对-运算符进行重载 43 Time Time::operator-(const Time & T) const 44 { 45 Time s; //创建一个局部对象,作为返回值 46 s.minitue = minitue - T.minitue; 47 s.hour = hour - T.hour + s.minitue/60; //虽然是减,但是为了以防万一,还是加上这个取整吧 48 s.minitue = s.minitue % 60; 49 50 return s; 51 } 52 53 //对*运算符进行重载 54 Time Time::operator*(double d) const 55 { 56 Time s; //创建一个局部对象,作为返回值 57 long total_time = hour * 60 * d + minitue * d;//这种都hour和minitue都相乘的方法是从书中学到的 58 s.hour = total_time / 60; 59 s.minitue = total_time % 60; 60 61 return s; 62 } 63 64 //只是增加小时 65 void Time::Addhour(int h) 66 { 67 hour = hour + h; 68 } 69 70 //只是增加分钟 71 void Time::Addminitue(int m) 72 { 73 minitue = minitue + m; 74 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 75 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 76 } 77 78 //重置时间 79 void Time::Reset(int h, int m) 80 { 81 hour = h; 82 minitue = m; 83 } 84 85 //友元函数的定义 86 //由于友元函数不是类成员函数,所以不能使用Time::限定符 87 //在友元函数的定义中也不能出现关键字friend 88 //但是友元函数却可以访问Time类中的私有数据和公有数据 89 Time operator*(double m, const Time & t) 90 { 91 Time result; 92 long total_time = t.hour * 60 * m + t.minitue * m; 93 result.hour = total_time / 60; 94 result.minitue = total_time % 60; 95 96 return result; 97 } 98 99 //显示对象中的数据 100 void Time::show() 101 { 102 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 103 }
1 //user_main.cpp 2 //使用运算符重载版本 3 //用s1=s2+s3代替s1 = s2.Sum(s3);即可 4 //s1=s2+s3;实际上是调用函数的方法:s1=s2.operator+(s3); 5 #include <iostream> 6 #include "mytime.h" 7 8 int main() 9 { 10 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 11 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 12 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 13 14 std::cout << "使用*运算符重载方法:" << std::endl; 15 s1 = s2 * 2; //等价于s1 = s1.operator*(2); 16 s1.show(); 17 18 s2.Reset(2,40); //对象s2调用类方法Reset(),并使用实参覆盖掉默认参数 19 20 std::cout << "使用友元函数:" << std::endl; 21 s1 = 2 * s2; //等价于s1 = operator*(2,s2); 22 s1.show(); 23 24 25 system("pause"); 26 return 0; 27 }
执行结果为:
对<<运算符的重载&友元函数
01)问题的提出:
在以前的程序版本中,加入要为显示一个Time对象trip的值,我们都是用的一个类方法show(),对象调用类方法
的方式为trip.show()。能不能用cout<<trip;呢,答案是可以的,因为<<也是C++运算符之一
02)运算符<<的历史和cout对象的相关介绍
最初<<运算符是c和c++位运算符,将值中的位左移。ostream类对该运算符进行了重载,将其转换为一个输出工具。
而cout又是类ostream的一个对象,能够识别C++基本类型。这是因为对于每种C++基本类型,ostream类声明中
都包含了相应的重载operator<<()定义。也就是说,一个定义使用int型参数,一个定义使用double型参数,一个定义
使用char型参数等等。因此要让cout能够识别TIme对象,一种方法是将一个新的函数运算符定义添加到ostream
类声明中,但修改ostream类是一个坏主意;一种方法是让Time知道任何使用cout,即在Time中声明对<<的重载函数
03)那么在Time类中声明友元函数还是非友元函数?
如果是声明常规的类方法,则在<<运算符的左边一定是一个Time对象,那么输出就是下面的那样了:
trip<<cout; //这样显然是不合适的
所以要使用友元函数:
void operator<<(ostream & os, const Time & t) //其中ostream是一个类
{
os << t.hour <<" hours" << t.minitue <<"minitues\n";
}
这样就可以使用下面的语句了:
cout<<trip; //显示对象trip中的数据了
调用cout<<trip应使用cout对象本身,而不是cout的副本,因此应该使用ostream & os以用,而不是按值传递
Time对象可以按值传递或者是按引用传递,按引用传递比按值传递使用的时间和内存都要少,因此使用按引用传递。
04)改进
很显然,上面对<<重载友元函数对于下面这样的语句是无能为力的:
cout<<"Trip time: "<<trip<<"(Tuesday)\n";
05)解决方法:让cout<<"Trip time: "返回一个cout即可解决问题。反应在对<<重载友元函数来说就是返回值为
指向ostream对象的引用即可,即下面改进的对<<重载友元函数版本:
ostream & operator<<(ostream & os, const Time & t) //其中ostream是一个类
{
os << t.hour <<" hours" << t.minitue <<"minitues\n";
return os;
}
注意:返回值不再是void了,而是指向ostream对象的引用
06)上面的这个operator<<()函数版本还可以用于将输出写如到文件中:
#include <fstream> //for ofstream
...
ofstream fout; //创建一个ofstream类对象fout
fout.open("savetime.txt"); //对象fout和一个txt文件关联
Time trip(12,40); //创建一个Time类对象trip,并隐式的调用构造函数初始化trip对象
fout<<trip; //实际调用方式为 operator<<(fout,trip);
1 //mytime.h 2 //包含了operator*()和operatro<<()两个友元函数 3 //将operator*()作为内联函数,因为其代码很短(定义也是原型时,要使用关键字friend) 4 5 #ifndef MYTIME_H_ 6 #define MYTIME_H_ 7 #include <iostream> //这里声明了,在mytime.cpp就只包含mytime.h头文件,便可以提供iostream头文件的支持 8 9 class Time 10 { 11 private: 12 int hour; 13 int minitue; 14 public: 15 Time(); //声明默认构造函数 16 Time(int h, int m); //声明构造函数 17 Time operator+(const Time & T) const; 18 Time operator-(const Time & T) const; //两个const可有可无 19 Time operator*(double d) const; //最后一个const还是表明不能修改调用operator*()方法的对象中的数据 20 friend Time operator*(double m, const Time & t) //即使原型是定义,要使用关键字friend,同时也是内联函数 21 { 22 return t * m; //实际上调用方法为t.operator*(m),即调用对*的重载函数 23 } 24 friend std::ostream & operator<<(std::ostream & os, Time & t);//声明一个对运算符<<重载的友元函数 25 void Addhour(int h); //单独的增加小时 26 void Addminitue(int m); //单独的增加分钟 27 void Reset(int h=0, int m=0); //重置时间 28 void show(); 29 }; 30 31 #endif 32 33 34 /* 友元函数 */ 35 /* 36 01)问题的提出: 37 对于上一个代码中的对*的函数重载中 Time operator*(double d) const; 38 对Time对象s1和s2,以及一个double值2.1 39 使用方法只能是s1 = s2*2.1;//实际上是调用对*的重载函数:s1=s2.operator(2.1); 40 所以s1 = 2.1*s2; 是会报错的 41 02)解决方法: 42 A 写注释:告诉每个人只能按照s2*2.1这种方式去写,不能写成2.1*s2 43 B 使用非成员函数,非成员函数不是由对象调用的,它使用的值都必须是由实参的形式传入的 44 但是也引发了一个新问题:非成员函数不能访问私有数据。最终的解决方法是使用友元函数 45 03)友元函数的声明方法:使用关键字friend 46 friend Time operator*(doubla m,const Time & t);//该友元函数同时对*运算符进行了重载 47 该声明意味着下面两点: 48 A 虽然operator*()是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用; 49 B 虽然operatir*()不是成员函数,但它有成员函数的访问权限。(可以访问私有数据) 50 04)友元函数的定义方法:不用使用关键字friend,因为它不是成员函数,所以也不用使用Time::限定符 51 Time operator*(double m, Time & t) const 52 { 53 Time result; 54 long total_time = t.hour * m + t.minitue * m; 55 result.hour = total_time / 60; 56 result.minitue = total_time % 60; 57 return result; 58 } 59 05)友元函数调用方法: 60 有了友元函数之后,就可以直接使用 s1 = 2.1*s2; 61 编译器将s1 = 2.1*s2;转换成s1 = operator*(2.1,s2);友元函数版本 62 06)稍作修改,就可以将友元函数改变成非友元函数: 63 //在该非友元函数中调用对*的重载函数 64 Time operator*(double m, Time & t) const 65 { 66 return t*m; 67 //原来的版本是显式的访问t.hour和t.minitue.由于这里是将对象t整体使用的,所以t*m将调用对*的重载函数 68 //即实际调用的函数为 t.operator*(m) 69 } 70 07)总结: 71 如果在类声明中即声明了对*的重载函数Time operator*(double d) const; 72 在类声明在又声明了友元函数:friend Time operator*(doubla m,const Time & t); 73 该友元函数同时对*运算符进行了重载,那么在主函数中就可以使用下面的两种方式 74 s1 = s2 * 2.1; //调用对*的重载函数,编译器将其转换为s1 = s2.operator(2.1); 75 s1 = 2.1 * s2; //调用友元函数,编译器将其转换为:s1 = operator(2.1,s2); 76 即实现了乘法的交换律。 77 */ 78 79 /* 对<<运算符的重载&友元函数 */ 80 /* 81 01)问题的提出: 82 在以前的程序版本中,加入要为显示一个Time对象trip的值,我们都是用的一个类方法show(),对象调用类方法 83 的方式为trip.show()。能不能用cout<<trip;呢,答案是可以的,因为<<也是C++运算符之一 84 02)运算符<<的历史和cout对象的相关介绍 85 最初<<运算符是c和c++位运算符,将值中的位左移。ostream类对该运算符进行了重载,将其转换为一个输出工具。 86 而cout又是类ostream的一个对象,能够识别C++基本类型。这是因为对于每种C++基本类型,ostream类声明中 87 都包含了相应的重载operator<<()定义。也就是说,一个定义使用int型参数,一个定义使用double型参数,一个定义 88 使用char型参数等等。因此要让cout能够识别TIme对象,一种方法是将一个新的函数运算符定义添加到ostream 89 类声明中,但修改ostream类是一个坏主意;一种方法是让Time知道任何使用cout,即在Time中声明对<<的重载函数 90 03)那么在Time类中声明友元函数还是非友元函数? 91 如果是声明常规的类方法,则在<<运算符的左边一定是一个Time对象,那么输出就是下面的那样了: 92 trip<<cout; //这样显然是不合适的 93 所以要使用友元函数: 94 void operator<<(ostream & os, const Time & t) //其中ostream是一个类 95 { 96 os << t.hour <<" hours" << t.minitue <<"minitues\n"; 97 } 98 这样就可以使用下面的语句了: 99 cout<<trip; //显示对象trip中的数据了 100 调用cout<<trip应使用cout对象本身,而不是cout的副本,因此应该使用ostream & os以用,而不是按值传递 101 Time对象可以按值传递或者是按引用传递,按引用传递比按值传递使用的时间和内存都要少,因此使用按引用传递。 102 04)改进 103 很显然,上面对<<重载友元函数对于下面这样的语句是无能为力的: 104 cout<<"Trip time: "<<trip<<"(Tuesday)\n"; 105 05)解决方法:让cout<<"Trip time: "返回一个cout即可解决问题。反应在对<<重载友元函数来说就是返回值为 106 指向ostream对象的引用即可,即下面改进的对<<重载友元函数版本: 107 ostream & operator<<(ostream & os, const Time & t) //其中ostream是一个类 108 { 109 os << t.hour <<" hours" << t.minitue <<"minitues\n"; 110 return os; 111 } 112 注意:返回值不再是void了,而是指向ostream对象的引用 113 06)上面的这个operator<<()函数版本还可以用于将输出写如到文件中: 114 #include <fstream> //for ofstream 115 ... 116 ofstream fout; //创建一个ofstream类对象fout 117 fout.open("savetime.txt"); //对象fout和一个txt文件关联 118 Time trip(12,40); //创建一个Time类对象trip,并隐式的调用构造函数初始化trip对象 119 fout<<trip; //实际调用方式为 operator<<(fout,trip); 120 121 */
1 //mytime.cpp 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 //重载-运算符Time Time::operator-(const Time & T) const 5 //重载*运算符Time Time::operator*(double d) const 6 //#include <iostream> //可以不包含该头文件了,因为在mytime.h头文件中引用了该头文件,且在本文件中包含了mytime.h头文件 7 #include "mytime.h" 8 9 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 10 { 11 hour = 0; 12 minitue = 0; 13 } 14 15 16 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 17 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 18 //即 int & h = 4; 这样是不合法的 19 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 20 { 21 hour = h; 22 minitue = m; 23 } 24 25 Time Time::operator+(const Time & T) const 26 { 27 Time s; //新建一个TIme对象,用于作为该函数的返回值 28 s.minitue = minitue + T.minitue; 29 s.hour = hour + T.hour + s.minitue / 60; 30 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 31 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 32 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 33 34 return s; 35 } 36 37 //对-运算符进行重载 38 Time Time::operator-(const Time & T) const 39 { 40 Time s; //创建一个局部对象,作为返回值 41 s.minitue = minitue - T.minitue; 42 s.hour = hour - T.hour + s.minitue/60; //虽然是减,但是为了以防万一,还是加上这个取整吧 43 s.minitue = s.minitue % 60; 44 45 return s; 46 } 47 48 //对*运算符进行重载 49 Time Time::operator*(double d) const 50 { 51 Time s; //创建一个局部对象,作为返回值 52 long total_time = hour * 60 * d + minitue * d;//这种都hour和minitue都相乘的方法是从书中学到的 53 s.hour = total_time / 60; 54 s.minitue = total_time % 60; 55 56 return s; 57 } 58 59 //只是增加小时,常规类方法 60 void Time::Addhour(int h) 61 { 62 hour = hour + h; 63 } 64 65 //只是增加分钟,常规类方法 66 void Time::Addminitue(int m) 67 { 68 minitue = minitue + m; 69 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 70 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 71 } 72 73 //重置时间,常规类方法 74 void Time::Reset(int h, int m) 75 { 76 hour = h; 77 minitue = m; 78 } 79 80 //友元函数的定义 81 //由于友元函数不是类成员函数,所以不能使用Time::限定符 82 //在友元函数的定义中也不能出现关键字friend 83 //但是友元函数却可以访问Time类中的私有数据和公有数据 84 //Time operator*(double m, const Time & t) //该函数已经在头文件中声明和定义 85 //{ 86 // Time result; 87 // long total_time = t.hour * 60 * m + t.minitue * m; 88 // result.hour = total_time / 60; 89 // result.minitue = total_time % 60; 90 // 91 // return result; 92 //} 93 94 //cout<<trip实现 95 //对<<运算符进行重载的友元函数定义 96 //由于ostream在名称空间std中,所以要使用std::限定符 97 //对于os,将被从主函数传入的实参代替,由于该实参是在转函数中产生,所以operator<<()函数执行完毕后 98 //该实参也存在,所以返回值的类型可以是引用 99 //如果是在一个子函数中创建的局部变量,则返回类型就不能是引用了 100 std::ostream & operator<<(std::ostream & os, Time & t) 101 { 102 os << t.hour << " hours" << t.minitue << " minitues\n"; 103 return os; 104 } 105 106 //显示对象中的数据 107 void Time::show() 108 { 109 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 110 }
1 //user_main.cpp 2 //使用运算符重载版本 3 //用s1=s2+s3代替s1 = s2.Sum(s3);即可 4 //s1=s2+s3;实际上是调用函数的方法:s1=s2.operator+(s3); 5 #include <iostream> 6 #include "mytime.h" 7 8 int main() 9 { 10 using std::cout; 11 using std::endl; 12 13 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 14 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 15 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 16 17 std::cout << "使用*运算符重载方法:" << std::endl; 18 s1 = s2 * 2; //等价于s1 = s1.operator*(2); 19 cout << s1; //和使用s1.show()的作用是一样的 20 21 s2.Reset(2,40); //对象s2调用类方法Reset(),并使用实参覆盖掉默认参数 22 23 std::cout << "使用友元函数:" << std::endl; 24 s1 = 2 * s2; //等价于s1 = operator*(2,s2); 25 cout << s1; //和使用s1.show()的作用是一样的 26 27 28 system("pause"); 29 return 0; 30 }
执行结果为:
cin.clear()的用法 m5
我们谈谈cin.clear的作用,第一次看到这东西,很多人以为就是清空cin里面的数据流,而实际上却与此相差很远,首先我们看看以下代码:
#include <iostream>
using
namespace
std;
int
main()
{
int
a;
cin>>a;
cout<<cin.rdstate()<<endl;
if
(cin.rdstate() == ios::goodbit)
{
cout<<
"输入数据的类型正确,无错误!"
<<endl;
}
if
(cin.rdstate() == ios_base::failbit)
{
cout<<
"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"
<<endl;
}
system
(
"pause"
);
}
goodbit 无错误
Eofbit 已到达文件尾
failbit 非致命的输入/输出错误,可挽回
badbit 致命的输入/输出错误,无法挽回 若在输入输出类里.需要加ios::标识符号
通过cin.clear,我们能确认它的内部标识符,如果输入错误则能重新输入.结合真正的清空数据流方法cin.sync(),请看下例:
#include <iostream>
using
namespace
std;
int
main()
{
int
a;
while
(1)
{
cin>>a;
if
(!cin)
//条件可改写为cin.fail()
{
cout<<
"输入有错!请重新输入"
<<endl;
cin.clear();
cin.sync();
//清空流
}
else
{
cout<<a;
break
;
}
}
system
(
"pause"
);
}
极坐标和直角坐标的相互转换(随机漫步的实现)
/* 总结 */
01)对象是不可以直接调用类中的私有变量的,只能调用公有函数!!
所以加入对象result要使用x值怎么办?result.x是不合法的,所以使用result.xval();
02)使用了名称空间来创建类,或在名称空间中创建类,那么类中的方法定义的时候,一种方法是和h文件中写的一样:使用
namespace VECT{ ... }; 另一种方法就是在cpp文件中使用using声明
03)如果使用using声明,那么定义友元函数的时候,在友元函数名之前是要加上名称空间的名字+双冒号的,否则友元函数
不能访问类中的私有数据
1 //vect.h 2 //极坐标和直角坐标的相互转换 3 4 #ifndef VECTOR_H_ 5 #define VECTOR_H_ 6 7 //定义一个名称空间VECTOR,将类定义放在名称空间中 8 namespace VECTOR 9 { 10 class Vector 11 { 12 public: 13 enum Mode {RECT,POL}; //定义枚举量Mode,可以用Mode去定义变量,例如Mode M; 但是只能用RECT和POL对M赋值 14 private: 15 double x; //直角坐标系的横坐标 16 double y; //直角坐标系的纵坐标 17 double mag; //极坐标系的长度 18 double ang; //极坐标系的角度 19 Mode mode; //用枚举量定义一个枚举变量,mode的值只能是RECT或POL 20 void set_mag(); //设置极坐标系的长度函数 21 void set_ang(); //设置极坐标系的角度函数 22 void set_x(); 23 void set_y(); 24 public: 25 Vector(); //默认构造函数的声明 26 ~Vector(); //析构函数的声明 27 //声明构造函数,n1和n2是传入的直角坐标系或者是极坐标系的坐标,默认是RECT(直角坐标系模式) 28 Vector(double n1, double n2, Mode form = RECT); 29 //声明重置函数,作用类似于析构函数,只不过reset()可以随时使用,而是构函数只有在类创建对象的时候才会被使用 30 void reset(double n1, double n2, Mode form = RECT); 31 //定义返回x、y、mag、ang的值的函数,由于是在类中定义,自动成为内联函数 32 //const放在了函数名括号的后面,表示该函数不可以修改调用该函数对象的参数 33 //对象是不可以直接调用类中的私有变量的,只能调用公有函数!! 34 //所以加入对象result要使用x值怎么办?result.x是不合法的,所以使用result.xval(); 35 double xval() const { return x; } 36 double yval() const { return y; } 37 double magval() const { return mag; } 38 double angval() const { return ang; } 39 //直角坐标系或者是极坐标系模式的选择 40 void polar_mode(); 41 void rect_mode(); 42 //运算符重载 43 Vector operator+(const Vector & b) const; //第一个Vector表示operator+()函数的返回值为Vector类对象 44 Vector operator-(const Vector & b) const; 45 Vector operator-() const; //对类对象中的数据进行去反操作,即正负的变换 46 Vector operator*(double n) const; 47 //友元函数的声明 48 friend Vector operator*(double n,const Vector & b); 49 friend std::ostream & operator<<(std::ostream & os, const Vector & v); 50 }; 51 } 52 53 #endif 54 55 /* 总结 */ 56 /* 57 01)对象是不可以直接调用类中的私有变量的,只能调用公有函数!! 58 所以加入对象result要使用x值怎么办?result.x是不合法的,所以使用result.xval(); 59 02)使用了名称空间来创建类,或在名称空间中创建类,那么类中的方法定义的时候,一种方法是和h文件中写的一样:使用 60 namespace VECT{ ... }; 另一种方法就是在cpp文件中使用using声明 61 03)如果使用using声明,那么定义友元函数的时候,在友元函数名之前是要加上名称空间的名字+双冒号的,否则友元函数 62 不能访问类中的私有数据 63 */
1 //vector.cpp 2 #include <iostream> 3 #include <cmath> //for sqrt()、sin()、cos()、atan()、atan()、 4 #include "vect.h" 5 6 7 using VECTOR::Vector; //声明名称空间中的类,以后就可以直接使用类中的方法 8 using std::sqrt; //开根号 9 using std::sin; 10 using std::cos; 11 using std::atan; //atan(x)表示求x的反正切,返回值为[-pi/2,pi/2]区间的一个值,返回弧度值 12 using std::atan2; //atan2(y,x)表示求y/x的正切,返回值为[-pi,pi]区间的一个值,y是纵坐标的值 13 using std::cout; 14 using std::endl; 15 16 //输入的是度数,而程序中要用弧度值,所以需要180/pi,例如将60°转换为弧度值方法为60/rad_to_deg 17 const double rad_to_deg = 180.0 / 3.14; 18 19 //默认构造函数定义 20 Vector::Vector() 21 { 22 x = y = mag = ang = 0; 23 mode = RECT; //刚刚这一句丢了 24 } 25 26 //析构函数定义 27 Vector::~Vector() 28 { 29 30 } 31 32 //构造函数定义 33 Vector::Vector(double n1, double n2, Mode form) 34 { 35 mode = form; 36 if (form == RECT) 37 { 38 x = n1; //将传入的直角坐标系的参数传递给类中的成员变量x和y 39 y = n2; 40 set_mag(); //设置x和y对应的极坐标系中的值 41 set_ang(); 42 } 43 else if (form == POL) 44 { 45 mag = n1; 46 ang = n2 / rad_to_deg; //将度数转换为弧度制 47 set_x(); 48 set_y(); 49 } 50 else 51 { 52 cout << "您输入有误,将坐标系的值全部设置为0" << endl; 53 x = y = mag = ang = 0; 54 } 55 56 } 57 58 //重置函数的定义 这个方法不能用 59 //void Vector::reset(double n1, double n2, Mode f) 60 //{ 61 // Vector(n1, n2, f); //调用构造函数 62 //} 63 void Vector::reset(double n1, double n2, Mode form) 64 { 65 mode = form; 66 if (form == RECT) 67 { 68 x = n1; //将传入的直角坐标系的参数传递给类中的成员变量x和y 69 y = n2; 70 set_mag(); //设置x和y对应的极坐标系中的值 71 set_ang(); 72 } 73 else if (form == POL) 74 { 75 mag = n1; 76 ang = n2 / rad_to_deg; //将度数转换为弧度制 77 set_x(); 78 set_y(); 79 } 80 else 81 { 82 cout << "您输入有误,将坐标系的值全部设置为0" << endl; 83 x = y = mag = ang = 0; 84 } 85 } 86 87 //设置极坐标系的长度函数 88 void Vector::set_mag() 89 { 90 mag = sqrt(x * x + y * y); 91 } 92 void Vector::set_ang() 93 { 94 ang = atan2(y, x); //返回(x,y)的正切值 95 } 96 void Vector::set_x() 97 { 98 x = mag * cos(ang); 99 } 100 void Vector::set_y() 101 { 102 y = mag * sin(ang); 103 } 104 105 //直角坐标系或者是极坐标系模式的选择 106 void Vector::polar_mode() 107 { 108 mode = POL; 109 } 110 void Vector::rect_mode() 111 { 112 mode = RECT; 113 } 114 115 //运算符重载 116 Vector Vector::operator+(const Vector & b) const 117 { 118 //Vector V; //新建一个类对象,作为返回值 119 //V.x = x + b.x; //自己的想法 120 121 return Vector(x + b.x, y + b.y); //调用构造函数,模式选择为默认值RECT 122 //不用去别另外的一个模式,因为在主函数中即使是用RECT模式去减 123 //接下来,RECT模式也会转换为POL模式中的参数的 124 //x和y的值为调用operator+()对象中的数据,b.x为作为实参传入的对象中的数据 125 126 } 127 Vector Vector::operator-(const Vector & b) const 128 { 129 return Vector(x - b.x, y - b.y); //调用构造函数,模式选择为默认值RECT 130 //x和y的值为调用operator-()对象中的数据,b.x为作为实参传入的对象中的数据 131 } 132 Vector Vector::operator-() const 133 { 134 return Vector(-x, -y); 135 //x和y的值为调用operator-()对象中的数据 136 } 137 Vector Vector::operator*(double n) const 138 { 139 return Vector(x*n, y*n); 140 //x和y的值为调用operator-()对象中的数据 141 } 142 143 //友元函数的声明 144 //Vector operator*(double n, const Vector & b) //刚刚没有使用名称空间对operator*()声明,导致b.x出错 145 //如果友元函数在名称空间中,必须要使用名称空间对友元函数的函数名进行声明,否则无法访问类中的数据 146 //还有一个方法是在cpp文件中也将namespace VECTOR{...},按照h文件中的写法,照样搬过来就可以不用加VECTOR:: 147 Vector VECTOR::operator*(double n, const Vector & b) 148 { 149 return Vector(b.x * n , b.y * n); 150 //return b * n; 151 } 152 std::ostream & VECTOR::operator<<(std::ostream & os, const Vector & v) 153 { 154 if (v.mode == Vector::RECT) 155 os << "(x,y) = " << "(" << v.x << "," << v.y << ")\n"; 156 else if (v.mode == Vector::POL) 157 os << "(mag,ang) = " << "(" << v.mag << "," << v.ang * rad_to_deg << ")\n"; //将rad转换为度数 158 else 159 os << "Vector object mode is invalid\n"; 160 return os; 161 }
1 #include <iostream> 2 #include <ctime> //for time() 3 #include <cstdlib> //for rand()、srand() 4 #include"vect.h" 5 6 int main() 7 { 8 using namespace std; 9 using VECTOR::Vector; 10 11 srand(time(0)); 12 13 Vector step; //使用默认构造函数创建对象,该对象中的数据全部为0,默认为直角坐标系 14 Vector result(0.0, 0.0); //隐式的调用构造函数创建对象,并将该对象中的变量设置为0,默认为直角坐标系 15 double direction = 0.0; //走的方向 16 double dstep = 0.0; //每步走的长度 17 double target = 0.0; //要走的长度,该长度为极坐标系中的长度 18 unsigned long steps = 0.0; //定义走的总步数 19 20 cout << "Enter target distance(任意子母键退出): "; 21 while (cin >> target) 22 { 23 cout << "Enter step length: "; 24 if (!(cin >> dstep)) 25 { 26 break; //如果输入的不是数字,那么退出while循环 27 } 28 //对象时不可以直接调用类中的私有变量的,只能调用公有函数!! 29 while (result.magval() < target) 30 { 31 direction = rand() % 360; //防止随机数大于360出现的情况 32 step.reset(dstep, direction, Vector::POL); //重置step对象在的数据 33 result = result + step; 34 /*steps++;*/ //走的总步数 35 cout << steps++ << endl; 36 cout << result.magval() << endl; 37 } 38 cout << "After " << steps << " steps, 对象到达了如下坐标(直角坐标系):"; 39 cout << result << endl; //输出对象result中的数据,默认result对象的模式为RECT(直角坐标系) 40 result.polar_mode(); //设置result对象的模式为极坐标系POL 41 cout << "或者在极坐标系下的坐标为:" << endl; 42 cout << result << endl; //极坐标系下输出 43 44 cout << "平均每步走的距离为:" << result.magval() / steps << endl; 45 46 //重置变量,为下一次循环做准备 47 steps = 0; 48 result.reset(0.0, 0.0); 49 cout << "Enter target distance(任意子母键退出): "; 50 } 51 52 cout << "Bye!\n"; 53 cin.clear(); 54 while (cin.get() != '\n') 55 continue; 56 system("pause"); 57 return 0; 58 }
---恢复内容结束---
目录
用类方法合并另个时间&运算符重载(涉及到函数返回值能不能是引用的问题)
用类方法合并另个时间的代码如下:
1 //mytime.h 2 #ifndef MYTIME_H_ 3 #define MYTIME_H_ 4 5 class Time 6 { 7 private: 8 int hour; 9 int minitue; 10 public: 11 Time(); //声明默认构造函数 12 //Time(int & h, int & m); //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 13 Time(int h, int m); //声明构造函数 14 Time Sum(Time & T); //声明返回值为类对象的函数,形参为指向类对象的引用 15 void Addhour(int h); //单独的增加小时 16 void Addminitue(int m); //单独的增加分钟 17 void Reset(int h=0, int m=0); //重置时间 18 void show(); 19 }; 20 21 #endif
1 //mytime.cpp 2 #include <iostream> 3 #include "mytime.h" 4 5 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 6 { 7 hour = 0; 8 minitue = 0; 9 } 10 11 12 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 13 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 14 //即 int & h = 4; 这样是不合法的 15 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 16 { 17 hour = h; 18 minitue = m; 19 } 20 21 Time Time::Sum(Time & T) 22 { 23 Time s; //新建一个TIme对象,用于作为该函数的返回值 24 s.minitue = minitue + T.minitue; 25 s.hour = hour + T.hour + s.minitue / 60; 26 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 27 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 28 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 29 30 return s; 31 } 32 //注意:Sum()函数的返回值不能是Time & (指向Time对象的引用),这是由于返回的对象时s,而s是一个在Sum() 33 //中定义的局部变量,Sum()函数执行完毕后,s将会消失,返回一个消失的引用是不合适的 34 //所以这里返回s对象的副本,之后在主函数中可以使用它 35 //以前函数的返回值可以为引用,是因为返回的对象均为从主函数中传入的对象,这些对象都是在主函数中定义的 36 //所以可以返回,比如this指针那里,传入一个对象和this指针指向的调用类方法的对象 37 38 //只是增加小时 39 void Time::Addhour(int h) 40 { 41 hour = hour + h; 42 } 43 44 //只是增加分钟 45 void Time::Addminitue(int m) 46 { 47 minitue = minitue + m; 48 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 49 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 50 } 51 52 //重置时间 53 void Time::Reset(int h, int m) 54 { 55 hour = h; 56 minitue = m; 57 } 58 59 //显示对象中的数据 60 void Time::show() 61 { 62 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 63 }
1 //user_main.cpp 2 #include <iostream> 3 #include "mytime.h" 4 5 int main() 6 { 7 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 8 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 9 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 10 11 s1 = s2.Sum(s3); //将Sum()中的this指向s2,s形参用实参s3代替 12 s1.show(); 13 14 s1.Addhour(3); //对s1对象中的数据,只是增加小时 15 s1.show(); 16 17 system("pause"); 18 return 0; 19 } 20 /* 总结 */ 21 /* 22 01)关于引用的使用方法:int & rt = 3; 这样使用是不合法的,要注意在函数参数传递的时候不要发生这样的错误 23 02)关于函数返回值的问题:如果一个变量(包括对象)是在该函数内创建的,那么是不可以以引用的方式返回的 24 因为引用返回的都是该变量本身,而该变量在对应的函数执行完毕之后就消失了,从而发生错误。 25 但是如果变量(对象)是从主函数中传入的,并且返回的也是从主函数中传入的变量(对象),那么是可以以引用 26 的方式返回的。 27 03) 28 */
/* 总结 */
01)关于引用的使用方法:int & rt = 3; 这样使用是不合法的,要注意在函数参数传递的时候不要发生这样的错误
02)关于函数返回值的问题:如果一个变量(包括对象)是在该函数内创建的,那么是不可以以引用的方式返回的
因为引用返回的都是该变量本身,而该变量在对应的函数执行完毕之后就消失了,从而发生错误。
但是如果变量(对象)是从主函数中传入的,并且返回的也是从主函数中传入的变量(对象),那么是可以以引用
的方式返回的。
执行结果:
运算符重载
01)要使用重载运算符,必须使用被称为运算符函数的特殊函数形式:
operaterop(argument-list)
其中operater为关键字,op是要重载的运算符,argument-list为形参,相当于创建一个名字为operaterop的函数
比如对+进行重载即:operater+(),该函数没有形参
op必须是有效的C++运算符,不能是@,但可以是[],因为[]是数组索引运算符
02)运算符重载实际上仍然是函数调用,只不过换了一种形式
假如对+进行重载即:operater+(Time & s) 其中Time是一个类
那么就可以说使用如下方式对两个对象进行相加:
s1 = s2 + s3; //其中s1、s2、s3都是Time类对象
编译器发现s1 s2 s3都是类对象,因此使用相应的运算符函数进行替换:
s1 = s2.operater+(s3); //所以说运算符重载实际上也还是函数调用
03)s1 = s2 + s3;该式隐式的使用s2(因为s2调用了类方法),显式的使用了类对象s3(因为s3作为参数传入)
当然s1 = s3 + s2; 也是可以的,因为s2和s3都是类对象
上句就相当于s1 = s3.operater+(s2);了,即s3调用方法,s2作为参数传入
/* 时间的运算,引入了+运算符重载、-运算符重载和*运算符重载 */
1 //mytime.h 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 #ifndef MYTIME_H_ 5 #define MYTIME_H_ 6 7 class Time 8 { 9 private: 10 int hour; 11 int minitue; 12 public: 13 Time(); //声明默认构造函数 14 Time(int h, int m); //声明构造函数 15 Time operator+(const Time & T) const; //对运算符进行重载,形参为执行类对象的引用,返回值为Time对象, 16 //const Time & T 表明方法operator+()也不能修改作为参数传入的对象中的数据 17 //最后一个const表明operater+()方法不能修改调用这个方法的对象中的数据 18 Time operator-(const Time & T) const; //两个const可有可无 19 Time operator*(double d) const; //最后一个const还是表明不能修改调用operator*()方法的对象中的数据 20 void Addhour(int h); //单独的增加小时 21 void Addminitue(int m); //单独的增加分钟 22 void Reset(int h=0, int m=0); //重置时间 23 void show(); 24 }; 25 26 #endif 27 28 /* 运算符重载 */ 29 /* 30 01)要使用重载运算符,必须使用被称为运算符函数的特殊函数形式: 31 operaterop(argument-list) 32 其中operater为关键字,op是要重载的运算符,argument-list为形参,相当于创建一个名字为operaterop的函数 33 比如对+进行重载即:operater+(),该函数没有形参 34 op必须是有效的C++运算符,不能是@,但可以是[],因为[]是数组索引运算符 35 02)运算符重载实际上仍然是函数调用,只不过换了一种形式 36 假如对+进行重载即:operater+(Time & s) 其中Time是一个类 37 那么就可以说使用如下方式对两个对象进行相加: 38 s1 = s2 + s3; //其中s1、s2、s3都是Time类对象 39 编译器发现s1 s2 s3都是类对象,因此使用相应的运算符函数进行替换: 40 s1 = s2.operater+(s3); //所以说运算符重载实际上也还是函数调用 41 03)s1 = s2 + s3;该式隐式的使用s2(因为s2调用了类方法),显式的使用了类对象s3(因为s3作为参数传入) 42 当然s1 = s3 + s2; 也是可以的,因为s2和s3都是类对象 43 上句就相当于s1 = s3.operater+(s2);了,即s3调用方法,s2作为参数传入 44 45 */
1 //mytime.cpp 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 //重载-运算符Time Time::operator-(const Time & T) const 5 //重载*运算符Time Time::operator*(double d) const 6 #include <iostream> 7 #include "mytime.h" 8 9 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 10 { 11 hour = 0; 12 minitue = 0; 13 } 14 15 16 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 17 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 18 //即 int & h = 4; 这样是不合法的 19 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 20 { 21 hour = h; 22 minitue = m; 23 } 24 25 Time Time::operator+(const Time & T) const 26 { 27 Time s; //新建一个TIme对象,用于作为该函数的返回值 28 s.minitue = minitue + T.minitue; 29 s.hour = hour + T.hour + s.minitue / 60; 30 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 31 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 32 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 33 34 return s; 35 } 36 //注意:Sum()函数的返回值不能是Time & (指向Time对象的引用),这是由于返回的对象时s,而s是一个在Sum() 37 //中定义的局部变量,Sum()函数执行完毕后,s将会消失,返回一个消失的引用是不合适的 38 //所以这里返回s对象的副本,之后在主函数中可以使用它 39 //以前函数的返回值可以为引用,是因为返回的对象均为从主函数中传入的对象,这些对象都是在主函数中定义的 40 //所以可以返回,比如this指针那里,传入一个对象和this指针指向的调用类方法的对象 41 42 //对-运算符进行重载 43 Time Time::operator-(const Time & T) const 44 { 45 Time s; //创建一个局部对象,作为返回值 46 s.minitue = minitue - T.minitue; 47 s.hour = hour - T.hour + s.minitue/60; //虽然是减,但是为了以防万一,还是加上这个取整吧 48 s.minitue = s.minitue % 60; 49 50 return s; 51 } 52 53 //对*运算符进行重载 54 Time Time::operator*(double d) const 55 { 56 Time s; //创建一个局部对象,作为返回值 57 long total_time = hour * 60 * d + minitue * d;//这种都hour和minitue都相乘的方法是从书中学到的 58 s.hour = total_time / 60; 59 s.minitue = total_time % 60; 60 61 return s; 62 } 63 64 //只是增加小时 65 void Time::Addhour(int h) 66 { 67 hour = hour + h; 68 } 69 70 //只是增加分钟 71 void Time::Addminitue(int m) 72 { 73 minitue = minitue + m; 74 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 75 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 76 } 77 78 //重置时间 79 void Time::Reset(int h, int m) 80 { 81 hour = h; 82 minitue = m; 83 } 84 85 //显示对象中的数据 86 void Time::show() 87 { 88 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 89 }
1 //user_main.cpp 2 //使用运算符重载版本 3 //用s1=s2+s3代替s1 = s2.Sum(s3);即可 4 //s1=s2+s3;实际上是调用函数的方法:s1=s2.operator+(s3); 5 #include <iostream> 6 #include "mytime.h" 7 8 int main() 9 { 10 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 11 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 12 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 13 14 std::cout << "使用+重载运算符:" << std::endl; 15 s1 = s2 + s3; //等价于s1=s2.operator+(s3); s2和s3可以互换位置 16 s1.show(); 17 18 std::cout << "使用类方法Addhour():" << std::endl; 19 s1.Addhour(3); //对s1对象中的数据,只是增加小时 20 s1.show(); 21 22 std::cout << "使用-运算符重载方法:" << std::endl; 23 s1 = s1 - s2; //等价于s1=s1.operator-(s2);s1和s2可以互换位置 24 s1.show(); 25 26 std::cout << "使用*运算符重载方法:" << std::endl; 27 s1 = s1 * 2; //等价于s1 = s1.operator-(2); 28 s1.show(); 29 //这里s1只能是在*的左边,2只能是在*的右边 30 //在*的左边的标识符是要调用operator*()方法的,显然数字不能调用该方法 31 //此项缺陷也为以后的友元函数的提出打下了基础 32 33 system("pause"); 34 return 0; 35 } 36 /* 总结 */ 37 /* 38 01)关于引用的使用方法:int & rt = 3; 这样使用是不合法的,要注意在函数参数传递的时候不要发生这样的错误 39 02)关于函数返回值的问题:如果一个变量(包括对象)是在该函数内创建的,那么是不可以以引用的方式返回的 40 因为引用返回的都是该变量本身,而该变量在对应的函数执行完毕之后就消失了,从而发生错误。 41 但是如果变量(对象)是从主函数中传入的,并且返回的也是从主函数中传入的变量(对象),那么是可以以引用 42 的方式返回的。 43 03) 44 */
执行结果为:
友元函数
01)问题的提出:
对于上一个代码中的对*的函数重载中 Time operator*(double d) const;
对Time对象s1和s2,以及一个double值2.1
使用方法只能是s1 = s2*2.1;//实际上是调用对*的重载函数:s1=s2.operator(2.1);
所以s1 = 2.1*s2; 是会报错的
02)解决方法:
A 写注释:告诉每个人只能按照s2*2.1这种方式去写,不能写成2.1*s2
B 使用非成员函数,非成员函数不是由对象调用的,它使用的值都必须是由实参的形式传入的
但是也引发了一个新问题:非成员函数不能访问私有数据。最终的解决方法是使用友元函数
此处引入友元函数的目的是实现乘法的交换律
C 友元函数是一种介于类非成员函数和类成员函数之间的一种函数,友元函数不是类成员函数,但是可以访问类私有数据(拥有 类成员函数的权限)
03)友元函数的声明方法:使用关键字friend
friend Time operator*(doubla m,const Time & t);//该友元函数同时对*运算符进行了重载
该声明意味着下面两点:
A 虽然operator*()是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用; ***
B 虽然operatir*()不是成员函数,但它有成员函数的访问权限。(可以访问私有数据) ***
04)友元函数的定义方法:不用使用关键字friend,因为它不是成员函数,所以也不用使用Time::限定符
Time operator*(double m, Time & t) const
{
Time result;
long total_time = t.hour * m + t.minitue * m;
result.hour = total_time / 60;
result.minitue = total_time % 60;
return result;
}
05)友元函数调用方法:
有了友元函数之后,就可以直接使用 s1 = 2.1*s2;
编译器将s1 = 2.1*s2;转换成s1 = operator*(2.1,s2);友元函数版本
06)稍作修改,就可以将友元函数改变成非友元函数:
//在该非友元函数中调用对*的重载函数
Time operator*(double m, Time & t) const
{
return t*m; //即实际调用的函数为 t.operator*(m),即调用的函数是对*的重载的函数
//原来的版本是显式的访问t.hour和t.minitue.由于这里是将对象t整体使用的,所以t*m将调用对*的重载函数
}
07)总结:
如果在类声明中即声明了对*的重载函数Time operator*(double d) const;
在类声明在又声明了友元函数:friend Time operator*(doubla m,const Time & t);
该友元函数同时对*运算符进行了重载,那么在主函数中就可以使用下面的两种方式
s1 = s2 * 2.1; //调用对*的重载函数,编译器将其转换为s1 = s2.operator(2.1);
s1 = 2.1 * s2; //调用友元函数,编译器将其转换为:s1 = operator(2.1,s2);
即实现了乘法的交换律。
1 //mytime.h 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 #ifndef MYTIME_H_ 5 #define MYTIME_H_ 6 7 class Time 8 { 9 private: 10 int hour; 11 int minitue; 12 public: 13 Time(); //声明默认构造函数 14 Time(int h, int m); //声明构造函数 15 Time operator+(const Time & T) const; 16 Time operator-(const Time & T) const; //两个const可有可无 17 Time operator*(double d) const; //最后一个const还是表明不能修改调用operator*()方法的对象中的数据 18 friend Time operator*(double m, const Time & t); //声明一个友元函数,只允许中声明的时候使用friend关键字 19 void Addhour(int h); //单独的增加小时 20 void Addminitue(int m); //单独的增加分钟 21 void Reset(int h=0, int m=0); //重置时间 22 void show(); 23 }; 24 25 #endif 26 27 28 /* 友元函数 */ 29 /* 30 01)问题的提出: 31 对于上一个代码中的对*的函数重载中 Time operator*(double d) const; 32 对Time对象s1和s2,以及一个double值2.1 33 使用方法只能是s1 = s2*2.1;//实际上是调用对*的重载函数:s1=s2.operator(2.1); 34 所以s1 = 2.1*s2; 是会报错的 35 02)解决方法: 36 A 写注释:告诉每个人只能按照s2*2.1这种方式去写,不能写成2.1*s2 37 B 使用非成员函数,非成员函数不是由对象调用的,它使用的值都必须是由实参的形式传入的 38 但是也引发了一个新问题:非成员函数不能访问私有数据。最终的解决方法是使用友元函数 39 03)友元函数的声明方法:使用关键字friend 40 friend Time operator*(doubla m,const Time & t);//该友元函数同时对*运算符进行了重载 41 该声明意味着下面两点: 42 A 虽然operator*()是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用; 43 B 虽然operatir*()不是成员函数,但它有成员函数的访问权限。(可以访问私有数据) 44 04)友元函数的定义方法:不用使用关键字friend,因为它不是成员函数,所以也不用使用Time::限定符 45 Time operator*(double m, Time & t) const 46 { 47 Time result; 48 long total_time = t.hour * m + t.minitue * m; 49 result.hour = total_time / 60; 50 result.minitue = total_time % 60; 51 return result; 52 } 53 05)友元函数调用方法: 54 有了友元函数之后,就可以直接使用 s1 = 2.1*s2; 55 编译器将s1 = 2.1*s2;转换成s1 = operator*(2.1,s2);友元函数版本 56 06)稍作修改,就可以将友元函数改变成非友元函数: 57 //在该非友元函数中调用对*的重载函数 58 Time operator*(double m, Time & t) const 59 { 60 return t*m; 61 //原来的版本是显式的访问t.hour和t.minitue.由于这里是将对象t整体使用的,所以t*m将调用对*的重载函数 62 //即实际调用的函数为 t.operator*(m) 63 } 64 07)总结: 65 如果在类声明中即声明了对*的重载函数Time operator*(double d) const; 66 在类声明在又声明了友元函数:friend Time operator*(doubla m,const Time & t); 67 该友元函数同时对*运算符进行了重载,那么在主函数中就可以使用下面的两种方式 68 s1 = s2 * 2.1; //调用对*的重载函数,编译器将其转换为s1 = s2.operator(2.1); 69 s1 = 2.1 * s2; //调用友元函数,编译器将其转换为:s1 = operator(2.1,s2); 70 即实现了乘法的交换律。 71 */
1 //mytime.cpp 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 //重载-运算符Time Time::operator-(const Time & T) const 5 //重载*运算符Time Time::operator*(double d) const 6 #include <iostream> 7 #include "mytime.h" 8 9 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 10 { 11 hour = 0; 12 minitue = 0; 13 } 14 15 16 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 17 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 18 //即 int & h = 4; 这样是不合法的 19 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 20 { 21 hour = h; 22 minitue = m; 23 } 24 25 Time Time::operator+(const Time & T) const 26 { 27 Time s; //新建一个TIme对象,用于作为该函数的返回值 28 s.minitue = minitue + T.minitue; 29 s.hour = hour + T.hour + s.minitue / 60; 30 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 31 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 32 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 33 34 return s; 35 } 36 //注意:Sum()函数的返回值不能是Time & (指向Time对象的引用),这是由于返回的对象时s,而s是一个在Sum() 37 //中定义的局部变量,Sum()函数执行完毕后,s将会消失,返回一个消失的引用是不合适的 38 //所以这里返回s对象的副本,之后在主函数中可以使用它 39 //以前函数的返回值可以为引用,是因为返回的对象均为从主函数中传入的对象,这些对象都是在主函数中定义的 40 //所以可以返回,比如this指针那里,传入一个对象和this指针指向的调用类方法的对象 41 42 //对-运算符进行重载 43 Time Time::operator-(const Time & T) const 44 { 45 Time s; //创建一个局部对象,作为返回值 46 s.minitue = minitue - T.minitue; 47 s.hour = hour - T.hour + s.minitue/60; //虽然是减,但是为了以防万一,还是加上这个取整吧 48 s.minitue = s.minitue % 60; 49 50 return s; 51 } 52 53 //对*运算符进行重载 54 Time Time::operator*(double d) const 55 { 56 Time s; //创建一个局部对象,作为返回值 57 long total_time = hour * 60 * d + minitue * d;//这种都hour和minitue都相乘的方法是从书中学到的 58 s.hour = total_time / 60; 59 s.minitue = total_time % 60; 60 61 return s; 62 } 63 64 //只是增加小时 65 void Time::Addhour(int h) 66 { 67 hour = hour + h; 68 } 69 70 //只是增加分钟 71 void Time::Addminitue(int m) 72 { 73 minitue = minitue + m; 74 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 75 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 76 } 77 78 //重置时间 79 void Time::Reset(int h, int m) 80 { 81 hour = h; 82 minitue = m; 83 } 84 85 //友元函数的定义 86 //由于友元函数不是类成员函数,所以不能使用Time::限定符 87 //在友元函数的定义中也不能出现关键字friend 88 //但是友元函数却可以访问Time类中的私有数据和公有数据 89 Time operator*(double m, const Time & t) 90 { 91 Time result; 92 long total_time = t.hour * 60 * m + t.minitue * m; 93 result.hour = total_time / 60; 94 result.minitue = total_time % 60; 95 96 return result; 97 } 98 99 //显示对象中的数据 100 void Time::show() 101 { 102 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 103 }
1 //user_main.cpp 2 //使用运算符重载版本 3 //用s1=s2+s3代替s1 = s2.Sum(s3);即可 4 //s1=s2+s3;实际上是调用函数的方法:s1=s2.operator+(s3); 5 #include <iostream> 6 #include "mytime.h" 7 8 int main() 9 { 10 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 11 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 12 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 13 14 std::cout << "使用*运算符重载方法:" << std::endl; 15 s1 = s2 * 2; //等价于s1 = s1.operator*(2); 16 s1.show(); 17 18 s2.Reset(2,40); //对象s2调用类方法Reset(),并使用实参覆盖掉默认参数 19 20 std::cout << "使用友元函数:" << std::endl; 21 s1 = 2 * s2; //等价于s1 = operator*(2,s2); 22 s1.show(); 23 24 25 system("pause"); 26 return 0; 27 }
执行结果为:
对<<运算符的重载&友元函数
01)问题的提出:
在以前的程序版本中,加入要为显示一个Time对象trip的值,我们都是用的一个类方法show(),对象调用类方法
的方式为trip.show()。能不能用cout<<trip;呢,答案是可以的,因为<<也是C++运算符之一
02)运算符<<的历史和cout对象的相关介绍
最初<<运算符是c和c++位运算符,将值中的位左移。ostream类对该运算符进行了重载,将其转换为一个输出工具。
而cout又是类ostream的一个对象,能够识别C++基本类型。这是因为对于每种C++基本类型,ostream类声明中
都包含了相应的重载operator<<()定义。也就是说,一个定义使用int型参数,一个定义使用double型参数,一个定义
使用char型参数等等。因此要让cout能够识别TIme对象,一种方法是将一个新的函数运算符定义添加到ostream
类声明中,但修改ostream类是一个坏主意;一种方法是让Time知道任何使用cout,即在Time中声明对<<的重载函数
03)那么在Time类中声明友元函数还是非友元函数?
如果是声明常规的类方法,则在<<运算符的左边一定是一个Time对象,那么输出就是下面的那样了:
trip<<cout; //这样显然是不合适的
所以要使用友元函数:
void operator<<(ostream & os, const Time & t) //其中ostream是一个类
{
os << t.hour <<" hours" << t.minitue <<"minitues\n";
}
这样就可以使用下面的语句了:
cout<<trip; //显示对象trip中的数据了
调用cout<<trip应使用cout对象本身,而不是cout的副本,因此应该使用ostream & os以用,而不是按值传递
Time对象可以按值传递或者是按引用传递,按引用传递比按值传递使用的时间和内存都要少,因此使用按引用传递。
04)改进
很显然,上面对<<重载友元函数对于下面这样的语句是无能为力的:
cout<<"Trip time: "<<trip<<"(Tuesday)\n";
05)解决方法:让cout<<"Trip time: "返回一个cout即可解决问题。反应在对<<重载友元函数来说就是返回值为
指向ostream对象的引用即可,即下面改进的对<<重载友元函数版本:
ostream & operator<<(ostream & os, const Time & t) //其中ostream是一个类
{
os << t.hour <<" hours" << t.minitue <<"minitues\n";
return os;
}
注意:返回值不再是void了,而是指向ostream对象的引用
06)上面的这个operator<<()函数版本还可以用于将输出写如到文件中:
#include <fstream> //for ofstream
...
ofstream fout; //创建一个ofstream类对象fout
fout.open("savetime.txt"); //对象fout和一个txt文件关联
Time trip(12,40); //创建一个Time类对象trip,并隐式的调用构造函数初始化trip对象
fout<<trip; //实际调用方式为 operator<<(fout,trip);
1 //mytime.h 2 //包含了operator*()和operatro<<()两个友元函数 3 //将operator*()作为内联函数,因为其代码很短(定义也是原型时,要使用关键字friend) 4 5 #ifndef MYTIME_H_ 6 #define MYTIME_H_ 7 #include <iostream> //这里声明了,在mytime.cpp就只包含mytime.h头文件,便可以提供iostream头文件的支持 8 9 class Time 10 { 11 private: 12 int hour; 13 int minitue; 14 public: 15 Time(); //声明默认构造函数 16 Time(int h, int m); //声明构造函数 17 Time operator+(const Time & T) const; 18 Time operator-(const Time & T) const; //两个const可有可无 19 Time operator*(double d) const; //最后一个const还是表明不能修改调用operator*()方法的对象中的数据 20 friend Time operator*(double m, const Time & t) //即使原型是定义,要使用关键字friend,同时也是内联函数 21 { 22 return t * m; //实际上调用方法为t.operator*(m),即调用对*的重载函数 23 } 24 friend std::ostream & operator<<(std::ostream & os, Time & t);//声明一个对运算符<<重载的友元函数 25 void Addhour(int h); //单独的增加小时 26 void Addminitue(int m); //单独的增加分钟 27 void Reset(int h=0, int m=0); //重置时间 28 void show(); 29 }; 30 31 #endif 32 33 34 /* 友元函数 */ 35 /* 36 01)问题的提出: 37 对于上一个代码中的对*的函数重载中 Time operator*(double d) const; 38 对Time对象s1和s2,以及一个double值2.1 39 使用方法只能是s1 = s2*2.1;//实际上是调用对*的重载函数:s1=s2.operator(2.1); 40 所以s1 = 2.1*s2; 是会报错的 41 02)解决方法: 42 A 写注释:告诉每个人只能按照s2*2.1这种方式去写,不能写成2.1*s2 43 B 使用非成员函数,非成员函数不是由对象调用的,它使用的值都必须是由实参的形式传入的 44 但是也引发了一个新问题:非成员函数不能访问私有数据。最终的解决方法是使用友元函数 45 03)友元函数的声明方法:使用关键字friend 46 friend Time operator*(doubla m,const Time & t);//该友元函数同时对*运算符进行了重载 47 该声明意味着下面两点: 48 A 虽然operator*()是在类声明中声明的,但它不是成员函数,因此不能使用成员运算符来调用; 49 B 虽然operatir*()不是成员函数,但它有成员函数的访问权限。(可以访问私有数据) 50 04)友元函数的定义方法:不用使用关键字friend,因为它不是成员函数,所以也不用使用Time::限定符 51 Time operator*(double m, Time & t) const 52 { 53 Time result; 54 long total_time = t.hour * m + t.minitue * m; 55 result.hour = total_time / 60; 56 result.minitue = total_time % 60; 57 return result; 58 } 59 05)友元函数调用方法: 60 有了友元函数之后,就可以直接使用 s1 = 2.1*s2; 61 编译器将s1 = 2.1*s2;转换成s1 = operator*(2.1,s2);友元函数版本 62 06)稍作修改,就可以将友元函数改变成非友元函数: 63 //在该非友元函数中调用对*的重载函数 64 Time operator*(double m, Time & t) const 65 { 66 return t*m; 67 //原来的版本是显式的访问t.hour和t.minitue.由于这里是将对象t整体使用的,所以t*m将调用对*的重载函数 68 //即实际调用的函数为 t.operator*(m) 69 } 70 07)总结: 71 如果在类声明中即声明了对*的重载函数Time operator*(double d) const; 72 在类声明在又声明了友元函数:friend Time operator*(doubla m,const Time & t); 73 该友元函数同时对*运算符进行了重载,那么在主函数中就可以使用下面的两种方式 74 s1 = s2 * 2.1; //调用对*的重载函数,编译器将其转换为s1 = s2.operator(2.1); 75 s1 = 2.1 * s2; //调用友元函数,编译器将其转换为:s1 = operator(2.1,s2); 76 即实现了乘法的交换律。 77 */ 78 79 /* 对<<运算符的重载&友元函数 */ 80 /* 81 01)问题的提出: 82 在以前的程序版本中,加入要为显示一个Time对象trip的值,我们都是用的一个类方法show(),对象调用类方法 83 的方式为trip.show()。能不能用cout<<trip;呢,答案是可以的,因为<<也是C++运算符之一 84 02)运算符<<的历史和cout对象的相关介绍 85 最初<<运算符是c和c++位运算符,将值中的位左移。ostream类对该运算符进行了重载,将其转换为一个输出工具。 86 而cout又是类ostream的一个对象,能够识别C++基本类型。这是因为对于每种C++基本类型,ostream类声明中 87 都包含了相应的重载operator<<()定义。也就是说,一个定义使用int型参数,一个定义使用double型参数,一个定义 88 使用char型参数等等。因此要让cout能够识别TIme对象,一种方法是将一个新的函数运算符定义添加到ostream 89 类声明中,但修改ostream类是一个坏主意;一种方法是让Time知道任何使用cout,即在Time中声明对<<的重载函数 90 03)那么在Time类中声明友元函数还是非友元函数? 91 如果是声明常规的类方法,则在<<运算符的左边一定是一个Time对象,那么输出就是下面的那样了: 92 trip<<cout; //这样显然是不合适的 93 所以要使用友元函数: 94 void operator<<(ostream & os, const Time & t) //其中ostream是一个类 95 { 96 os << t.hour <<" hours" << t.minitue <<"minitues\n"; 97 } 98 这样就可以使用下面的语句了: 99 cout<<trip; //显示对象trip中的数据了 100 调用cout<<trip应使用cout对象本身,而不是cout的副本,因此应该使用ostream & os以用,而不是按值传递 101 Time对象可以按值传递或者是按引用传递,按引用传递比按值传递使用的时间和内存都要少,因此使用按引用传递。 102 04)改进 103 很显然,上面对<<重载友元函数对于下面这样的语句是无能为力的: 104 cout<<"Trip time: "<<trip<<"(Tuesday)\n"; 105 05)解决方法:让cout<<"Trip time: "返回一个cout即可解决问题。反应在对<<重载友元函数来说就是返回值为 106 指向ostream对象的引用即可,即下面改进的对<<重载友元函数版本: 107 ostream & operator<<(ostream & os, const Time & t) //其中ostream是一个类 108 { 109 os << t.hour <<" hours" << t.minitue <<"minitues\n"; 110 return os; 111 } 112 注意:返回值不再是void了,而是指向ostream对象的引用 113 06)上面的这个operator<<()函数版本还可以用于将输出写如到文件中: 114 #include <fstream> //for ofstream 115 ... 116 ofstream fout; //创建一个ofstream类对象fout 117 fout.open("savetime.txt"); //对象fout和一个txt文件关联 118 Time trip(12,40); //创建一个Time类对象trip,并隐式的调用构造函数初始化trip对象 119 fout<<trip; //实际调用方式为 operator<<(fout,trip); 120 121 */
1 //mytime.cpp 2 //使用运算符重载版本 3 //用 Time operater+(Time & s) const 代替Time Sum(Time & T);即可 4 //重载-运算符Time Time::operator-(const Time & T) const 5 //重载*运算符Time Time::operator*(double d) const 6 //#include <iostream> //可以不包含该头文件了,因为在mytime.h头文件中引用了该头文件,且在本文件中包含了mytime.h头文件 7 #include "mytime.h" 8 9 Time::Time() //默认构造函数的定义,创建类对象时,默认执行以下操作 10 { 11 hour = 0; 12 minitue = 0; 13 } 14 15 16 /*Time::Time(int & h, int & m) *///构造函数定义,创建类对象时,使用构造函数时执行以下操作. 17 //声明构造函数,如果是这样声明构造函数的话,在主函数中调用构造函数,传入一个数字是不合法的 18 //即 int & h = 4; 这样是不合法的 19 Time::Time(int h, int m) //这样在声明类对象的时候,调用该构造函数才是合法的,因为将数字传递给引用是不合法的 20 { 21 hour = h; 22 minitue = m; 23 } 24 25 Time Time::operator+(const Time & T) const 26 { 27 Time s; //新建一个TIme对象,用于作为该函数的返回值 28 s.minitue = minitue + T.minitue; 29 s.hour = hour + T.hour + s.minitue / 60; 30 //hour是调用Sum()方法的对象中的数据,T.hour是作为实参传入的对象中的数据 31 //最后再加上调用对象的分钟数,和,作为实参传入的分钟数的和,然后取整 32 s.minitue = s.minitue % 60; //对两个对象中数据的分钟数取余 33 34 return s; 35 } 36 37 //对-运算符进行重载 38 Time Time::operator-(const Time & T) const 39 { 40 Time s; //创建一个局部对象,作为返回值 41 s.minitue = minitue - T.minitue; 42 s.hour = hour - T.hour + s.minitue/60; //虽然是减,但是为了以防万一,还是加上这个取整吧 43 s.minitue = s.minitue % 60; 44 45 return s; 46 } 47 48 //对*运算符进行重载 49 Time Time::operator*(double d) const 50 { 51 Time s; //创建一个局部对象,作为返回值 52 long total_time = hour * 60 * d + minitue * d;//这种都hour和minitue都相乘的方法是从书中学到的 53 s.hour = total_time / 60; 54 s.minitue = total_time % 60; 55 56 return s; 57 } 58 59 //只是增加小时,常规类方法 60 void Time::Addhour(int h) 61 { 62 hour = hour + h; 63 } 64 65 //只是增加分钟,常规类方法 66 void Time::Addminitue(int m) 67 { 68 minitue = minitue + m; 69 hour = hour + minitue / 60; //如果增加的分钟数超过了60则对minitue以60为底取整 70 minitue = minitue % 60; //如果增加的分钟数超过了60则对minitue以60为底取余 71 } 72 73 //重置时间,常规类方法 74 void Time::Reset(int h, int m) 75 { 76 hour = h; 77 minitue = m; 78 } 79 80 //友元函数的定义 81 //由于友元函数不是类成员函数,所以不能使用Time::限定符 82 //在友元函数的定义中也不能出现关键字friend 83 //但是友元函数却可以访问Time类中的私有数据和公有数据 84 //Time operator*(double m, const Time & t) //该函数已经在头文件中声明和定义 85 //{ 86 // Time result; 87 // long total_time = t.hour * 60 * m + t.minitue * m; 88 // result.hour = total_time / 60; 89 // result.minitue = total_time % 60; 90 // 91 // return result; 92 //} 93 94 //cout<<trip实现 95 //对<<运算符进行重载的友元函数定义 96 //由于ostream在名称空间std中,所以要使用std::限定符 97 //对于os,将被从主函数传入的实参代替,由于该实参是在转函数中产生,所以operator<<()函数执行完毕后 98 //该实参也存在,所以返回值的类型可以是引用 99 //如果是在一个子函数中创建的局部变量,则返回类型就不能是引用了 100 std::ostream & operator<<(std::ostream & os, Time & t) 101 { 102 os << t.hour << " hours" << t.minitue << " minitues\n"; 103 return os; 104 } 105 106 //显示对象中的数据 107 void Time::show() 108 { 109 std::cout << "hour= " << hour << " minitue= " << minitue << std::endl; 110 }
1 //user_main.cpp 2 //使用运算符重载版本 3 //用s1=s2+s3代替s1 = s2.Sum(s3);即可 4 //s1=s2+s3;实际上是调用函数的方法:s1=s2.operator+(s3); 5 #include <iostream> 6 #include "mytime.h" 7 8 int main() 9 { 10 using std::cout; 11 using std::endl; 12 13 Time s1; //定义一个Time类对象,并用默认构造函数进行初始化 14 Time s2(2, 40); //定义一个Time类对象,并隐式的调用构造函数 15 Time s3(5, 55); //定义一个Time类对象,并隐式的调用构造函数 16 17 std::cout << "使用*运算符重载方法:" << std::endl; 18 s1 = s2 * 2; //等价于s1 = s1.operator*(2); 19 cout << s1; //和使用s1.show()的作用是一样的 20 21 s2.Reset(2,40); //对象s2调用类方法Reset(),并使用实参覆盖掉默认参数 22 23 std::cout << "使用友元函数:" << std::endl; 24 s1 = 2 * s2; //等价于s1 = operator*(2,s2); 25 cout << s1; //和使用s1.show()的作用是一样的 26 27 28 system("pause"); 29 return 0; 30 }
执行结果为:
cin.clear()的用法
我们谈谈cin.clear的作用,第一次看到这东西,很多人以为就是清空cin里面的数据流,而实际上却与此相差很远,首先我们看看以下代码:
#include <iostream>
using
namespace
std;
int
main()
{
int
a;
cin>>a;
cout<<cin.rdstate()<<endl;
if
(cin.rdstate() == ios::goodbit)
{
cout<<
"输入数据的类型正确,无错误!"
<<endl;
}
if
(cin.rdstate() == ios_base::failbit)
{
cout<<
"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"
<<endl;
}
system
(
"pause"
);
}
goodbit 无错误
Eofbit 已到达文件尾
failbit 非致命的输入/输出错误,可挽回
badbit 致命的输入/输出错误,无法挽回 若在输入输出类里.需要加ios::标识符号
通过cin.clear,我们能确认它的内部标识符,如果输入错误则能重新输入.结合真正的清空数据流方法cin.sync(),请看下例:
#include <iostream>
using
namespace
std;
int
main()
{
int
a;
while
(1)
{
cin>>a;
if
(!cin)
//条件可改写为cin.fail()
{
cout<<
"输入有错!请重新输入"
<<endl;
cin.clear();
cin.sync();
//清空流
}
else
{
cout<<a;
break
;
}
}
system
(
"pause"
);
}
极坐标和直角坐标的相互转换(随机漫步的实现)
/* 总结 */
01)对象是不可以直接调用类中的私有变量的,只能调用公有函数!!
所以加入对象result要使用x值怎么办?result.x是不合法的,所以使用result.xval();
02)使用了名称空间来创建类,或在名称空间中创建类,那么类中的方法定义的时候,一种方法是和h文件中写的一样:使用
namespace VECT{ ... }; 另一种方法就是在cpp文件中使用using声明
03)如果使用using声明,那么定义友元函数的时候,在友元函数名之前是要加上名称空间的名字+双冒号的,否则友元函数
不能访问类中的私有数据
1 //vect.h 2 //极坐标和直角坐标的相互转换 3 4 #ifndef VECTOR_H_ 5 #define VECTOR_H_ 6 7 //定义一个名称空间VECTOR,将类定义放在名称空间中 8 namespace VECTOR 9 { 10 class Vector 11 { 12 public: 13 enum Mode {RECT,POL}; //定义枚举量Mode,可以用Mode去定义变量,例如Mode M; 但是只能用RECT和POL对M赋值 14 private: 15 double x; //直角坐标系的横坐标 16 double y; //直角坐标系的纵坐标 17 double mag; //极坐标系的长度 18 double ang; //极坐标系的角度 19 Mode mode; //用枚举量定义一个枚举变量,mode的值只能是RECT或POL 20 void set_mag(); //设置极坐标系的长度函数 21 void set_ang(); //设置极坐标系的角度函数 22 void set_x(); 23 void set_y(); 24 public: 25 Vector(); //默认构造函数的声明 26 ~Vector(); //析构函数的声明 27 //声明构造函数,n1和n2是传入的直角坐标系或者是极坐标系的坐标,默认是RECT(直角坐标系模式) 28 Vector(double n1, double n2, Mode form = RECT); 29 //声明重置函数,作用类似于析构函数,只不过reset()可以随时使用,而是构函数只有在类创建对象的时候才会被使用 30 void reset(double n1, double n2, Mode form = RECT); 31 //定义返回x、y、mag、ang的值的函数,由于是在类中定义,自动成为内联函数 32 //const放在了函数名括号的后面,表示该函数不可以修改调用该函数对象的参数 33 //对象是不可以直接调用类中的私有变量的,只能调用公有函数!! 34 //所以加入对象result要使用x值怎么办?result.x是不合法的,所以使用result.xval(); 35 double xval() const { return x; } 36 double yval() const { return y; } 37 double magval() const { return mag; } 38 double angval() const { return ang; } 39 //直角坐标系或者是极坐标系模式的选择 40 void polar_mode(); 41 void rect_mode(); 42 //运算符重载 43 Vector operator+(const Vector & b) const; //第一个Vector表示operator+()函数的返回值为Vector类对象 44 Vector operator-(const Vector & b) const; 45 Vector operator-() const; //对类对象中的数据进行去反操作,即正负的变换 46 Vector operator*(double n) const; 47 //友元函数的声明 48 friend Vector operator*(double n,const Vector & b); 49 friend std::ostream & operator<<(std::ostream & os, const Vector & v); 50 }; 51 } 52 53 #endif 54 55 /* 总结 */ 56 /* 57 01)对象是不可以直接调用类中的私有变量的,只能调用公有函数!! 58 所以加入对象result要使用x值怎么办?result.x是不合法的,所以使用result.xval(); 59 02)使用了名称空间来创建类,或在名称空间中创建类,那么类中的方法定义的时候,一种方法是和h文件中写的一样:使用 60 namespace VECT{ ... }; 另一种方法就是在cpp文件中使用using声明 61 03)如果使用using声明,那么定义友元函数的时候,在友元函数名之前是要加上名称空间的名字+双冒号的,否则友元函数 62 不能访问类中的私有数据 63 */
1 //vector.cpp 2 #include <iostream> 3 #include <cmath> //for sqrt()、sin()、cos()、atan()、atan()、 4 #include "vect.h" 5 6 7 using VECTOR::Vector; //声明名称空间中的类,以后就可以直接使用类中的方法 8 using std::sqrt; //开根号 9 using std::sin; 10 using std::cos; 11 using std::atan; //atan(x)表示求x的反正切,返回值为[-pi/2,pi/2]区间的一个值,返回弧度值 12 using std::atan2; //atan2(y,x)表示求y/x的正切,返回值为[-pi,pi]区间的一个值,y是纵坐标的值 13 using std::cout; 14 using std::endl; 15 16 //输入的是度数,而程序中要用弧度值,所以需要180/pi,例如将60°转换为弧度值方法为60/rad_to_deg 17 const double rad_to_deg = 180.0 / 3.14; 18 19 //默认构造函数定义 20 Vector::Vector() 21 { 22 x = y = mag = ang = 0; 23 mode = RECT; //刚刚这一句丢了 24 } 25 26 //析构函数定义 27 Vector::~Vector() 28 { 29 30 } 31 32 //构造函数定义 33 Vector::Vector(double n1, double n2, Mode form) 34 { 35 mode = form; 36 if (form == RECT) 37 { 38 x = n1; //将传入的直角坐标系的参数传递给类中的成员变量x和y 39 y = n2; 40 set_mag(); //设置x和y对应的极坐标系中的值 41 set_ang(); 42 } 43 else if (form == POL) 44 { 45 mag = n1; 46 ang = n2 / rad_to_deg; //将度数转换为弧度制 47 set_x(); 48 set_y(); 49 } 50 else 51 { 52 cout << "您输入有误,将坐标系的值全部设置为0" << endl; 53 x = y = mag = ang = 0; 54 } 55 56 } 57 58 //重置函数的定义 这个方法不能用 59 //void Vector::reset(double n1, double n2, Mode f) 60 //{ 61 // Vector(n1, n2, f); //调用构造函数 62 //} 63 void Vector::reset(double n1, double n2, Mode form) 64 { 65 mode = form; 66 if (form == RECT) 67 { 68 x = n1; //将传入的直角坐标系的参数传递给类中的成员变量x和y 69 y = n2; 70 set_mag(); //设置x和y对应的极坐标系中的值 71 set_ang(); 72 } 73 else if (form == POL) 74 { 75 mag = n1; 76 ang = n2 / rad_to_deg; //将度数转换为弧度制 77 set_x(); 78 set_y(); 79 } 80 else 81 { 82 cout << "您输入有误,将坐标系的值全部设置为0" << endl; 83 x = y = mag = ang = 0; 84 } 85 } 86 87 //设置极坐标系的长度函数 88 void Vector::set_mag() 89 { 90 mag = sqrt(x * x + y * y); 91 } 92 void Vector::set_ang() 93 { 94 ang = atan2(y, x); //返回(x,y)的正切值 95 } 96 void Vector::set_x() 97 { 98 x = mag * cos(ang); 99 } 100 void Vector::set_y() 101 { 102 y = mag * sin(ang); 103 } 104 105 //直角坐标系或者是极坐标系模式的选择 106 void Vector::polar_mode() 107 { 108 mode = POL; 109 } 110 void Vector::rect_mode() 111 { 112 mode = RECT; 113 } 114 115 //运算符重载 116 Vector Vector::operator+(const Vector & b) const 117 { 118 //Vector V; //新建一个类对象,作为返回值 119 //V.x = x + b.x; //自己的想法 120 121 return Vector(x + b.x, y + b.y); //调用构造函数,模式选择为默认值RECT 122 //不用去别另外的一个模式,因为在主函数中即使是用RECT模式去减 123 //接下来,RECT模式也会转换为POL模式中的参数的 124 //x和y的值为调用operator+()对象中的数据,b.x为作为实参传入的对象中的数据 125 126 } 127 Vector Vector::operator-(const Vector & b) const 128 { 129 return Vector(x - b.x, y - b.y); //调用构造函数,模式选择为默认值RECT 130 //x和y的值为调用operator-()对象中的数据,b.x为作为实参传入的对象中的数据 131 } 132 Vector Vector::operator-() const 133 { 134 return Vector(-x, -y); 135 //x和y的值为调用operator-()对象中的数据 136 } 137 Vector Vector::operator*(double n) const 138 { 139 return Vector(x*n, y*n); 140 //x和y的值为调用operator-()对象中的数据 141 } 142 143 //友元函数的声明 144 //Vector operator*(double n, const Vector & b) //刚刚没有使用名称空间对operator*()声明,导致b.x出错 145 //如果友元函数在名称空间中,必须要使用名称空间对友元函数的函数名进行声明,否则无法访问类中的数据 146 //还有一个方法是在cpp文件中也将namespace VECTOR{...},按照h文件中的写法,照样搬过来就可以不用加VECTOR:: 147 Vector VECTOR::operator*(double n, const Vector & b) 148 { 149 return Vector(b.x * n , b.y * n); 150 //return b * n; 151 } 152 std::ostream & VECTOR::operator<<(std::ostream & os, const Vector & v) 153 { 154 if (v.mode == Vector::RECT) 155 os << "(x,y) = " << "(" << v.x << "," << v.y << ")\n"; 156 else if (v.mode == Vector::POL) 157 os << "(mag,ang) = " << "(" << v.mag << "," << v.ang * rad_to_deg << ")\n"; //将rad转换为度数 158 else 159 os << "Vector object mode is invalid\n"; 160 return os; 161 }
1 #include <iostream> 2 #include <ctime> //for time() 3 #include <cstdlib> //for rand()、srand() 4 #include"vect.h" 5 6 int main() 7 { 8 using namespace std; 9 using VECTOR::Vector; 10 11 srand(time(0)); 12 13 Vector step; //使用默认构造函数创建对象,该对象中的数据全部为0,默认为直角坐标系 14 Vector result(0.0, 0.0); //隐式的调用构造函数创建对象,并将该对象中的变量设置为0,默认为直角坐标系 15 double direction = 0.0; //走的方向 16 double dstep = 0.0; //每步走的长度 17 double target = 0.0; //要走的长度,该长度为极坐标系中的长度 18 unsigned long steps = 0.0; //定义走的总步数 19 20 cout << "Enter target distance(任意子母键退出): "; 21 while (cin >> target) 22 { 23 cout << "Enter step length: "; 24 if (!(cin >> dstep)) 25 { 26 break; //如果输入的不是数字,那么退出while循环 27 } 28 //对象时不可以直接调用类中的私有变量的,只能调用公有函数!! 29 while (result.magval() < target) 30 { 31 direction = rand() % 360; //防止随机数大于360出现的情况 32 step.reset(dstep, direction, Vector::POL); //重置step对象在的数据 33 result = result + step; 34 /*steps++;*/ //走的总步数 35 cout << steps++ << endl; 36 cout << result.magval() << endl; 37 } 38 cout << "After " << steps << " steps, 对象到达了如下坐标(直角坐标系):"; 39 cout << result << endl; //输出对象result中的数据,默认result对象的模式为RECT(直角坐标系) 40 result.polar_mode(); //设置result对象的模式为极坐标系POL 41 cout << "或者在极坐标系下的坐标为:" << endl; 42 cout << result << endl; //极坐标系下输出 43 44 cout << "平均每步走的距离为:" << result.magval() / steps << endl; 45 46 //重置变量,为下一次循环做准备 47 steps = 0; 48 result.reset(0.0, 0.0); 49 cout << "Enter target distance(任意子母键退出): "; 50 } 51 52 cout << "Bye!\n"; 53 cin.clear(); 54 while (cin.get() != '\n') 55 continue; 56 system("pause"); 57 return 0; 58 }
将double、int等数据类型赋值给类对象
01)以前声明对象并使用构造函数对其进行初始化的方法:
Stonewt blossem(132.5); //使用一个参数的构造函数对对象blossem进行初始化
Stonewt blossem = Stonewt(132.5); //显式的调用构造函数对类对象进行初始化
Stonewt buttercup(10, 2); //使用两个参数的构造函数对对象buttercup进行初始化
Stonewt bubbles; //使用默认构造函数对bubbles进行初始化
02)如果一个参数的构造函数创建了类对象,那么该对象可以接受一个double型、int型等数据类型对其进行赋值,如:
Stonewt myCat; //声明一个对象
myCat = 19.6; //使用一个数对类对象进行赋值,前提是该类中得有只有一个形参的构造函数
//以上代码使用Stonewt(double lbs)创建一个类对象,并将19.6作为初始值。这一过程是自动进行的,即隐式的
03)使用explicit来声明只有一个形参的构造函数,目的是防止意外的类型转换。如:
explicit Stonewt(double lbs); //这将关闭上边的隐式转换
Stonewt myCat;
myCat = 19.6; //此时是不合法的,因为使用了关键字explicit声明只有一个形参的构造函数
myCat = Stonewt(19.6); //合法,显式的进行类型强制转换
myCat = Stonewt(7000); //合法,先将7000转换为double类型,然后进行强制转换
myCat = (Stonewt)19.6; //合法,老式的方法
04)如果使用了关键字explicit声明构造函数,那么该构造函数就只能用来进行显式强制转换
05)如果类方法中定义了一个下面的带一个参数的构造函数:
Stonewt(double lbs);
那么再定义一个下面的只带一个参数的构造函数是不允许的:
Stonewt(long lbs);
1 #ifndef STONEWT_H_ 2 #define STONEWT_H_ 3 4 class Stonewt 5 { 6 private: 7 enum { Lbs_per_Stn = 14 }; //用枚举的方法定义一个常量,14后面不用加分号 8 int stone; 9 double pds_left; 10 double pounds; 11 public: 12 Stonewt(double lbs); //声明一个参数的构造函数 13 Stonewt(int stn, double lbs); //声明两个参数的构造函数 14 Stonewt(); //声明默认构造函数 15 ~Stonewt(); //声明析构函数 16 void show_lbs() const; 17 void show_stn() const; 18 }; 19 20 #endif 21 22 /* 将double、int等数据类型赋值给类对象 */ 23 /* 24 01)以前声明对象并使用构造函数对其进行初始化的方法: 25 Stonewt blossem(132.5); //使用一个参数的构造函数对对象blossem进行初始化 26 Stonewt blossem = Stonewt(132.5); //显式的调用构造函数对类对象进行初始化 27 Stonewt buttercup(10, 2); //使用两个参数的构造函数对对象buttercup进行初始化 28 Stonewt bubbles; //使用默认构造函数对bubbles进行初始化 29 02)如果一个参数的构造函数创建了类对象,那么该对象可以接受一个double型、int型等数据类型对其进行赋值,如: 30 Stonewt myCat; //声明一个对象 31 myCat = 19.6; //使用一个数对类对象进行赋值,前提是该类中得有只有一个形参的构造函数 32 //以上代码使用Stonewt(double lbs)创建一个类对象,并将19.6作为初始值。这一过程是自动进行的,即隐式的 33 03)使用explicit来声明只有一个形参的构造函数,目的是防止意外的类型转换。如: 34 explicit Stonewt(double lbs); //这将关闭上边的隐式转换 35 36 Stonewt myCat; 37 myCat = 19.6; //此时是不合法的,因为使用了关键字explicit声明只有一个形参的构造函数 38 myCat = Stonewt(19.6); //合法,显式的进行类型强制转换 39 myCat = Stonewt(7000); //合法,先将7000转换为double类型,然后进行强制转换 40 myCat = (Stonewt)19.6; //合法,老式的方法 41 04)如果使用了关键字explicit声明构造函数,那么该构造函数就只能用来进行显式强制转换 42 05)如果类方法中定义了一个下面的带一个参数的构造函数: 43 Stonewt(double lbs); 44 那么再定义一个下面的只带一个参数的构造函数是不允许的: 45 Stonewt(long lbs); 46 05) 47 48 */
1 //stonewt.cpp 2 #include <iostream> 3 #include "stonewt.h" 4 5 using std::cout; 6 using std::endl; 7 8 //接受一个参数的构造函数 9 //可以使用数字对类对象进行初始化,即: 10 //Stonewt myCat; 11 //myCat=19.6; //该句表明,上句是使用只有一个形参的构造函数创建的对象 12 Stonewt::Stonewt(double lbs) 13 { 14 stone = int(lbs) / Lbs_per_Stn; 15 pds_left = int(lbs) % Lbs_per_Stn; 16 pounds = lbs; 17 } 18 19 //接受一个参数的构造函数(使用关键字explicit) 20 //可以使用数字对类对象进行初始化,即: 21 //Stonewt myCat = Stonewt(19.6); //使用关键字explicit之后,就只能只有初始化对象 22 /*explicit Stonewt::Stonewt(double lbs) 23 { 24 stone = int(lbs) / Lbs_per_Stn; 25 pds_left = int(lbs) % Lbs_per_Stn; 26 pounds = lbs; 27 }*/ 28 29 //接受两个参数的构造函数 30 Stonewt::Stonewt(int stn, double lbs) 31 { 32 stone = stn; 33 pds_left = lbs; 34 pounds = stn * Lbs_per_Stn + lbs; 35 } 36 37 //默认构造函数 38 Stonewt::Stonewt() 39 { 40 stone = pounds = pds_left = 0; 41 } 42 43 //析构函数 44 Stonewt::~Stonewt() 45 { 46 47 } 48 49 //普通类方法 50 void Stonewt::show_stn() const 51 { 52 cout << stone << " stone, " << pds_left << " pounds" << endl; 53 } 54 void Stonewt::show_lbs() const 55 { 56 cout << pounds << " pounds" << endl; 57 }
1 //user_main,cpp 2 #include <iostream> 3 #include "stonewt.h" 4 5 using std::cout; 6 using std::endl; 7 8 void display(const Stonewt & st, int n);//第一个形参可以接受Stonewt对象或者是数字都可以 9 //如果第一个形参接受的实参为一个数字,在这里也是允许的,即: 10 // Stonewt & st = 422; 类似于Stonewt st=422; //使用接受一个参数的构造函数对st进行初始化 11 12 int main() 13 { 14 Stonewt incognito = 275; //先对275转换成double型,然后再用接受一个参数的构造函数对类对象进行初始化 15 //Stonewt(double lbs)即lbs=275 16 Stonewt wolfe(285.7); //以前初始化对象的方法,等价于Stonewt wolfe = 285.7; 17 Stonewt taft(21, 8); //使用接受两个参数的构造函数对类对象进行初始化 18 //Stonewt(int stn, double lbs)即stn=21,lbs=8 19 20 cout << "The celebrity weighted "; 21 incognito.show_stn(); 22 23 cout << "The detective weighted "; 24 wolfe.show_stn(); 25 26 cout << "The president weighted "; 27 taft.show_stn(); 28 29 incognito = 276.8; //同样是调用Stonewt(double lbs)构造函数,对类对象进行重新初始化 30 taft = 325; //调用Stonewt(double lbs)构造函数,对类对象taft进行重新初始化,虽然他taft不是使用接受一个参数的构造函数创建的 31 32 display(taft, 2); //display()第一个形参指向taft对象 33 display(422, 2); //display()第一个实参为数字,也是可以的,只是在类对象初始化是可以的 34 35 36 system("pause"); 37 return 0; 38 } 39 40 void display(const Stonewt & st, int n) 41 { 42 for (int i = 0; i < n; i++) 43 { 44 cout << "Wow! "; 45 st.show_stn(); 46 } 47 } 48 49 /* 总结 */ 50 /* 51 01)如果类中有只有一个参数的构造函数,那么下面的引用的用法是可以的 52 Stonewt & st = 422; //类似于Stonewt st=422; 使用接受一个参数的构造函数对st进行初始化 53 编译器会将该数字(422)通过Stonewt(double lbs)构造函数,编程Stonewt对象,然后 再赋给引用st 54 02)类对象使用数字对其进行初始化了之后,允许以后再次使用数字对其进行初始化,如下: 55 incognito = 276.8; 56 */
/* 总结 */
01)如果类中有只有一个参数的构造函数,那么下面的引用的用法是可以的
Stonewt & st = 422; //类似于Stonewt st=422; 使用接受一个参数的构造函数对st进行初始化
编译器会将该数字(422)通过Stonewt(double lbs)构造函数,编程Stonewt对象,然后 再赋给引用st
02)类对象使用数字对其进行初始化了之后,允许以后再次使用数字对其进行初始化,如下:
incognito = 276.8;
将类对象赋值给double、int等型的变量 m8
01)问题的提出:
上一节中提到了可以将double、int型的数据赋值给类对象,那么反过来呢?
即:Stonewt wolfe(285.7);
double host = wolfe; //这样也是可以的,只不过得在类中声明转换函数
02)转换函数的声明方法:
operator typeName(); //typeName为要转换成的数据类型,如double、int等
如:operator double(); //声明由类对象转换成double型数据的类方法
注意以下几点:
A 转换函数是类方法
B 转换函数不能指定返回类型且没有任何的参数
03)转换函数的定义方法:(由于转换函数是类方法,所以在定义时必须使用类限定符Stonewt::)
Stonewt::operator double()
{ return pounds;} //一般是在函数体中返回一直类中的私有变量
04)转换函数的使用方法:
Stonewt wolfe(285.7);
double host = wolfe; //隐式的调用转换函数
double host = double(wolfe) //显式的调用转换函数
需要注意的是,如果定义了多个转换函数:
operator int(); //#1
operator double(); //#2
隐式的调用转换函数是不合法的,因为编译器不知道是调用#1还是调用#2,从而产生二义性,产生报错
05)原则上说最好使用显式转换,为此,C++提供了关键字explicit用于转换函数,如:
explicit operator double(); //不能使用隐式的调用该函数,必须使用显式的转换方法
explicit operator int(); //同理
即:
Stonewt wolfe(285.7);
double host = wolfe; //如果转换函数前加了关键字explicit,隐式的调用转换函数是不合法的
double host = double(wolfe) //显式的调用转换函数,合法
定义的时候使用如下方法:
explicit Stonewt::operator int() //注意explicit的位置
{ return pounds; }
06)除了使用转换函数,我们也可以使用类方法去实现将类对象赋值给常规变量
即下面两种方法是等价的:
explicit Stonewt::operator int() //定义转换函数实现将类对象赋值给常规变量
{ return pounds; }
double Stonewt::Stone_to_double() //定义方法实现将类对象赋值给常规变量
{ return double(pounds);}
第一种方法的调用方法是:
Stonewt wolfe(285.7);
double host = double(wolfe) //显式的调用转换函数,合法
第二种方法的调用方法是:
Stonewt wolfe(285.7);
double wo = wolfe.Stone_to_double(); //类对象wolfe调用类方法Stone_to_double()