第十一章使用类
11.1操作符重载
1,要使用操作符重载,需要使用被称为操作符函数的特殊函数形式。
操作符函数的格式如下:
operator op (argument-list)
其中op是将要重振的操作符op必须是有效的C++操作符,不能虚构一个新的符号。Operator [] () 函数将重载[]操作符,因为[]是数组索引操作符。
11.2计算时间:一个操作符重载范例
#include<stdio.h> #include<iostream> #include<string.h> #include<math.h> #include<string> #include<algorithm> #include<queue> #include<map> typedef long long ll; using namespace std; #ifndef MYTIME0_H #define MYTIME0_H class Time{ private: int hours; int minutes; public: Time(); Time(int h,int m = 0); void AddMin(int m); void AddHr(int h); void Reset(int h = 0,int m = 0); Time operator + (const Time &t) const; void Show() const; }; #endif // MYTIME0_H Time :: Time(){hours = minutes = 0;} Time :: Time(int h,int m){hours = h; minutes = m;} void Time :: AddMin(int m) { minutes += m; hours += minutes / 60; minutes %= 60; } void Time :: AddHr(int h) { hours += h; } void Time :: Reset(int h,int m) { hours = h; minutes = m; } Time Time :: operator + (const Time & t) const///重载了加法运算 { Time sum; sum.minutes = minutes + t.minutes; sum.hours = hours + t.hours + sum.minutes / 60; sum.minutes %= 60; return sum; } void Time :: Show()const { std :: cout << hours << " hours," << minutes << " minutes"; std :: cout << endl; } int main() { Time planning; Time coding(2,40); Time fixing(5,55); Time total; std :: cout << "planning " ; planning.Show(); coding = coding + fixing; std :: cout << "coding "; coding.Show(); total = coding; total = total.operator+(fixing); std :: cout << "total "; total.Show(); }
11.2.2 重载限制
1,重载后的操作符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载操作符。因此,不能将减法操作符(-)重载为计算两个double值得和,而不是他们的差。虽然这种限制将对创造性有所影响,但可以确保程序正常运行。
2,使用操作符时不能违反操作符原来的句法规则。例如,不能将求模操作符(%
)重载成使用一个操作数。同样,不能修改操作符的优先级。因此,如果将加号操作符重载成将两个类相加,则新的操作符与原来的加号具有相同的优先级。
3,不能定义新的操作符。例如,不能定义operator
**()函数来表示求幂。
4,不能重载下面的操作符:
l sizeof ——sizeof操作符
l . —— 成员操作符
l * —— 成员指针操作符
l :: —— 作用域解析操作符
l ?: —— 条件操作符
l typeid —— 一个RTTI操作符
l const_cast —— 强制类型转换操作符
l dynamic_cast —— 强制类型转换操作符
l reinterpret_cast —— 强制类型转换操作符
l static_cast —— 强制类型转换操作符
11.3友元简介
1,友元有3种:友元函数,友元类,友元成员函数。
2,通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。
3,首先为什么需要友元?
答:A=B*(2) 等同于 A=B.operator(2);
但是如果是A=2*B呢? 编译器就无法理解这个了。
所以我们需要非成员函数(记住,大多数操作符都可以通过成员或非成员函数来重载)。非成员函数不是由对象调用的,它使用的所有值(包括对象)都是显式参数。这样,编译器能够将下面的表达式:
A = 2.72 * B
与下面的非成员函数调用匹配:
A = operator * (2.75. B);
该函数的原型如下:
Time operator * (double m,const Time & t);
对于非成员重载操作符函数来说,操作符表达式左边的操作数对应于操作符函数的第一个参数,操作符表达式右边的操作数对应于操作符函数的第二个参数而原来的成员函数则按相反的顺序处理操作数,也就是说,double值乘以Time值。但由此引发一个新的问题:非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。但是,有一类特殊的非成员函数可以访问类的私有成员,他们被称为友元函数。
11.3.1创建友元
1,创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字friend;
friend Time operator *(double
m,const Time &t);//goes in class declaration
2,该原型意味着下面两点:
l 虽然operator *()函数是在类声明中声名的,但它不是成员函数,因此不能使用成员操作符来调用。
l 虽然operator*()函数不是成员函数,但它与成员函数的访问权限相同。
3,第二步,是编写函数定义,注意他不是成员函数,所以不要使用Time::限定符。另外,不要在定义中使用关键字friend,定义应该如下:
#include<stdio.h> #include<iostream> #include<string.h> #include<math.h> #include<string> #include<algorithm> #include<queue> #include<map> typedef long long ll; using namespace std; #ifndef MYTIME0_H #define MYTIME0_H class Time{ private: int hours; int minutes; public: Time(); Time(int h,int m = 0); void AddMin(int m); void AddHr(int h); void Reset(int h = 0,int m = 0); Time operator + (const Time &t) const; void Show() const; friend Time operator * (int n,const Time & t); }; #endif // MYTIME0_H Time :: Time(){hours = minutes = 0;} Time :: Time(int h,int m){hours = h; minutes = m;} void Time :: AddMin(int m) { minutes += m; hours += minutes / 60; minutes %= 60; } void Time :: AddHr(int h) { hours += h; } void Time :: Reset(int h,int m) { hours = h; minutes = m; } Time Time :: operator + (const Time & t) const///重载了加法运算 { Time sum; sum.minutes = minutes + t.minutes; sum.hours = hours + t.hours + sum.minutes / 60; sum.minutes %= 60; return sum; } void Time :: Show()const { std :: cout << hours << " hours," << minutes << " minutes"; std :: cout << endl; } Time operator * (int n,const Time &t) { Time result; long long totalminutes = t.hours * n * 60 + t.minutes * n; result.hours = totalminutes / 60; result.minutes = totalminutes % 60; return result; } int main() { Time planning; Time coding(2,40); Time fixing(5,55); Time total; std :: cout << "planning " ; planning.Show(); coding = coding + fixing; std :: cout << "coding "; coding.Show(); total = coding; total = total.operator+(fixing); std :: cout << "total "; total.Show(); total = 2 * coding; std :: cout << "total "; total.Show(); }
总结:类的友元函数是非成员函数,其访问权限与成员函数相同。
问:友元是否有悖于OOP?
答:说法过于片面,相反,应将友元函数看作类的扩展接口的组成部分。例如,从概念上看,double乘以Time和Time乘以double是完全相同的。也就是前一个要求有友元函数,后一个使用成员函数,这是C++句法的结果,而不是概念上的差别。通过使用友元函数和类方法,可以用同一个用户接口表达这两种操作。另外请记住,只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有数据。简而言之:类方法和友元只是表达类接口的两种不同机制。
11.3.2常用的友元:重载<<操作符
1, 如何重载<<操作符,达到cout<<Time(类)的作用?
2,
<<的第一种重载版本
要使Time类知道使用cout,必须使用友元函数。因为cout<<Time;使用两个对象,一个为ostream类对象(cout),如果使用一个Time成员函数来重载 <<,Time对象将是第一个操作数。必须用:Time <<cout;这很迷惑。但是我们可以使用友元函数,可以像下面这样:
void operator << (ostream &
os,const Time &t)
{
os << t.hours <<” hours.”
<<t.minutes << “ minutes”;
}
3,
<<的第二种重载版本
第一种版本我们无法这样使用:cout << “time” << Time”<< endl;
那么我们可以如何解决呢?
本质上同第一种:
ostream& operator << (ostream &os,const Time &t)
{
os << t.hours << "
hours." << t.minutes << " minutes";
return os;
}
11.4重载运算符:作为成员函数还是非成员函数
1,记住:非成员版本的重载操作符函数所需的形参数目与操作符使用的操作数数目相同;而成员版本所需的参数数目少一个,因为其中的一个操作数儿被隐式地传递的调用对象。
11.6类的自动转换和强制类型转换
1, Time t(5); Time t = Time(5);
2, 可以用转换函数(自己定义)将结构体转换为一个double型。
转换函数:operator typeName();
转换函数必须是类方法
转换函数不能指定返回类型
转换函数不能有参数
例如转换为double类型的函数原型如下:
operator double(); 这里指出了要转换成的类型,因此不需要制定返回类型。转换函数是类方法意味着:它需要通过类对象来调用,从而告知函数要转换的值。因此,函数不需要参数。
3, 总结:
C++为类提供了下面的类型转换:
l 只有一个参数的类构造函数用于将类型与该参数相同的值转换为类类型。例如,将int值赋给Stonewt对象是,接受int参数的Stonewt类构造函数将自动被调用。不过在构造函数生明中使用explicit可防止隐式转换,而只允许显示转换。
l 被称为转换函数的特殊类操作符函数,用于将类对象转换为其他类型。转换函数是类成员,没有返回类型,没有参数,名为operator typeName(),其中,typeName 是对象将被自动转换成的类型。将类对象赋值给typeName变量或将其强制转换为typeName类型时,该转换函数将被自动被调用。