前面一章终于学习了类,但类博大精深,一章基本也就知道了怎么定义使用最简单的类。这一章就稍微深入探讨一下类之所以强大的许多特性。
1. 运算符重载
运算符重载是一种形式的多态,它允许赋予C++运算符多种含义。比如我们想做这样的加法evening = sam + janet ,这三个都是三个对象,这时候我们就要对加号重载。
运算符函数的格式如下
operatorop(argument-list)
这里op就是各种C++支持的运算符(加减乘除啥的呢)
书中举了一个计算时间的例子,把两个几小时几分的对象相加,这个对象属于Time class,包括hours和minutes的private数据成员还有其他一系列方法成员
class Time
{
private:
int hours;
int minutes;
public
Time operator+(const Time & t) const;
...
}
上面的operator+就是这个加号运算符重载函数的原型,下面是函数定义
Time Time::operator+(const Time & t) const
{
Time sum;
sum.minutes = minutes + t.minutes;
sum.hours = hours + sum.minutes/60;
sum.minutes %= 60;
return sum;
}
这一看跟普通的sum函数一模一样么,但用起来就很简单了
Time t1, t2, t3;
t1 = ...; t2 = ...;
t3 = t2 + t1;
可以直接用加号把两个Time对象相加了,并返回正确的time
其实t3 = t2 + t1对编译器来说是这样的t3 = t2.operator+(t1);
其实如果有t4的话,t4 = t3 + t2 + t1这样也是可以的
重载也有些限制:
重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。
使用运算符是不能违反运算符原来的句法规则,也不能修改运算符的优先级。
不能创建新的运算符
不能重载下面的运算符: sizeof sizeof运算符; . 成员运算符;
.* 成员指针运算符;:: 作用域解析运算符
等等等等
大多数运算符都可以通过成员或非成员函数进行重载,但下面的运算符只能通过成员函数进行重载:
= 赋值运算符
() 函数调用运算符
[] 下标运算符
-> 通过指针访问类成员的运算符
2. 友元
类经常会有标记为private的私有成员,之所以放在private下面,就是不想让外面访问。可c++又开了一个后门,即另外一种形式的访问权限:友元。
友元有3种: 友元函数,友元类,友元成员函数。这里只介绍友元函数
书里面举了一个重载乘法运算符的例子,如果A和B是同一个类的两个对象,这个类包含一个重载乘法运算符的方法,那么一般可以这样A = B*2,但如果要写A = 2*B的话,还需要定义一个新的函数,可以在类外面定义一个非成员的乘法运算符重载函数。如果要让这个非成员函数也要访问类私有变量的话,就得把它标记为类的友元函数。例如书中的例子:
friend Time operator*(double m, const Time & t);
这就表明这个operator*是Time的友元,可以访问Time类t对象的私有成员
注意不要在函数定义时再写关键字friend
一个经常用的友元的例子就是对<<运算符的重载。还是举Time类的例子,现在有一个trip的对象,我们想通过
cout << trip; 来显示trip是几时几分。
cout其实是ostream类的一个对象。如果使用一个Time成员函数来重载<<,Time对象将是第一个操作数,结果就是trip << cout。 所以我们必须使用一个非成员函数来重载<<,这个非成员函数还必须是Time类的friend,才能访问Time类的所有私有成员。这个函数可以这样定义
void operator << (ostream & os, const Time & t)
{ os << t.hours << " hours, " << t.minutes << " minutes"; }
但这个是不能这样
cout << "Trip time : " << trip << " (Tuesday)\n";
如果这样必须要重载函数返回一个ostream的对象
ostream & operator<< (ostream & os, const Time & t)
{
os << t.hours << " hours, " << t.minutes << " minutes";
return os;}
之后本章举了一个很大的vector的例子来详细说明了类的基本使用,运算符重载,友元函数,自己敲一边基本就明白啦。组合函数实现用了一个醉鬼走路的例子,还是很有意思的
3. 类的自动转换和强制类型转换
这一小节就是将类和普通类型的转换。
比如可以通过构造函数来讲一个普通类型的变量赋给一个类的对象
也可以将一个类的对象赋给一个普通类型的变量,这个时候就要在类里面额外添加一个转换函数了。要注意到是;
转换函数必须是类方法
转换函数不能指定返回类型
转换函数不能有参数,
例如转换为double类型的函数的原型如下:operator double();
调用是赋值符合=的左边之间放对象的名字就行。