---恢复内容开始---
目录
- 用类方法合并另个时间(涉及到函数返回值能不能是引用的问题)
- 运算符重载
- 友元函数
- 对<<运算符的重载&友元函数
- 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()
来源:oschina
链接:https://my.oschina.net/u/4312653/blog/3577488