本章内容包括:
- 运算符重载
- 友元函数
- 重载<<运算符
- 状态成员
- 使用rand()生成随机值
- 类的自动转换和强制类型转换
- 类转换函数
11.1 运算符重载
C++根据操作数的数目和类型决定采用哪种操作。
如果district2、sid和sara都是Salesperson类对象:
- 重载表示法:district2 = sid + sara;
- 函数表示法:distirct2 = sid.operator+(sara);
11.2 计算时间:一个运算符重载示例
//程序清单 11.1 mytime0.h
#pragma once
#ifndef MYTIME0_H_
#define MYTIME0_H_
using namespace std;
class Time
{
public:
Time();
~Time();
Time(int h, int m = 0);
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time Sum(const Time& t) const;
void Show() const;
private:
int hours;
int minutes;
};
#endif // !MYTIME0_H_
//程序清单 11.2
#include <iostream>
#include "mytime0.h"
Time::Time()
{
hours = minutes = 0;
}
Time::~Time()
{
}
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::Sum(const Time& t) const
{
Time temp;
temp.minutes = minutes + t.minutes;
temp.hours = hours + t.hours + temp.minutes / 60;
temp.minutes %= 60;
return temp;
}
void Time::Show() const
{
cout << hours << " hours, " << minutes << " minutes.\n";
}
Time Time::Sum(const Time& t) const
参数是引用,但返回类型不是,因为temp是临时对象。
不要返回指向局部变量或临时对象的引用。
//程序清单 11.3
#include <iostream>
#include "mytime0.h"
int main(void)
{
Time planning;
Time coding(2, 40);
Time fixing(5, 55);
Time total;
cout << "planning time = ";
planning.Show();
cout << "coding time = ";
coding.Show();
cout << "fixing time = ";
fixing.Show();
total = coding.Sum(fixing);
cout << "coding.Sum(fixing) = ";
total.Show();
return 0;
}
11.2.1 添加加法运算符
//程序清单 mytime1.h
#pragma once
#ifndef MYTIME1_H_
#define MYTIME1_H_
using namespace std;
class Time
{
public:
Time();
~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; //operator+代替sum
void Show() const;
private:
int hours;
int minutes;
};
#endif // !MYTIME1_H_
#include <iostream>
#include "mytime1.h"
Time::Time()
{
hours = minutes = 0;
}
Time::~Time()
{
}
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 temp;
temp.minutes = minutes + t.minutes;
temp.hours = hours + t.hours + temp.minutes / 60;
temp.minutes %= 60;
return temp;
}
void Time::Show() const
{
cout << hours << " hours, " << minutes << " minutes.\n";
}
#include <iostream>
#include "mytime1.h"
int main(void)
{
Time planning;
Time coding(2, 40);
Time fixing(5, 55);
Time total;
cout << "planning time = ";
planning.Show();
cout << "coding time = ";
coding.Show();
cout << "fixing time = ";
fixing.Show();
total = coding.operator+(fixing); //函数表示法
cout << "coding.Sum(fixing) = ";
total.Show();
total = coding + fixing; //运算符重载表示法
cout << "coding + fixing = ";
total.Show();
return 0;
}
11.2.2 重载限制
- 重载后的运算符至少有一个操作数是用户定义的类型,防止将标准类型重载运算符。
- 重载运算符不能违反原来的句法规则,不能修改优先级。
- 不能创建新的运算符。
- 不能重载的运算符:sizeof、成员运算符(.)、成员指针运算符(.*)、作用域解析运算符(::)、条件运算符(?:)
- 只能通过成员函数进行重载:赋值运算符(=)、函数调用运算符()、下标运算符[ ]、指针访问类成员运算符(->)
11.2.3 其他重载运算符
//程序清单 11.7 mytime2.h
#pragma once
#ifndef MYTIME2_H_
#define MYTIME2_H_
using namespace std;
class Time
{
public:
Time();
~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;
Time operator-(const Time& t) const;
Time operator*(double mult) const;
void Show() const;
private:
int hours;
int minutes;
};
#endif // !MYTIME2_H_
//程序清单 11.8
#include <iostream>
#include "mytime2.h"
Time::Time()
{
hours = minutes = 0;
}
Time::~Time()
{
}
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;
}
Time Time::operator-(const Time& t) const
{
Time diff;
int tot1, tot2;
tot1 = hours * 60 + minutes;
tot2 = t.hours * 60 + t.minutes;
diff.minutes = (tot1 - tot2) % 60;
diff.hours = (tot1 - tot2) / 60;
return diff;
}
Time Time::operator*(double mult) const
{
Time result;
long totalminutes = hours * 60 * mult + minutes * mult;
result.minutes = totalminutes % 60;
result.hours = totalminutes / 60;
return result;
}
void Time::Show() const
{
cout << hours << " hours, " << minutes << " minutes.\n";
}
//程序清单 11.9
#include <iostream>
#include "mytime2.h"
int main(void)
{
Time weeding(4, 35);
Time waxing(2, 47);
Time total, diff, adjusted;
cout << "weeding time = ";
weeding.Show();
cout << "waxing time = ";
waxing.Show();
cout << "total work time = ";
total = weeding + waxing;
total.Show();
cout << "weeding time - wax time = ";
diff = weeding - waxing;
diff.Show();
cout << "adjusted work time = ";
adjusted = total * 1.5;
adjusted.Show();
return 0;
}
11.3 友元
友元有三种:
- 友元函数
- 友元类
- 友元成员函数
友元函数与类成员函数具有相同权限。(友元不是成员,胜似成员)
- A = B * 2.75;
- A= B.operator*(2.75); //必须左侧为运算符对象,右侧为double类型
- A = 2.75 * B; //错误
11.3.1 创建友元
friend Time operator*(double m, const Time & t);
- operator*()函数是在类声明中声明,但不是成员函数,不能使用成员运算符来调用
- operator*()函数不是成员函数,但与成员函数的访问权限相同
- 类声明中友元声明前加friend,友元函数定义时不使用friend,也不使用Time::限定符
- 编写非成员函数,都尽量写成友元函数
11.3.2 常用的友元:重载<<运算符
//程序清单 11.10 mytime3.h
#pragma once
#ifndef MYTIME3_H_
#define MYTIME3_H_
using namespace std;
class Time
{
public:
Time();
~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;
Time operator-(const Time& t) const;
Time operator*(double mult) const;
friend Time operator*(double mult, const Time& t) { return t * mult; }
friend ostream& operator<<(ostream& os, const Time& t);
private:
int hours;
int minutes;
};
#endif // !MYTIME3_H_
//程序清单 11.11
#include <iostream>
#include "mytime3.h"
Time::Time()
{
hours = minutes = 0;
}
Time::~Time()
{
}
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;
}
Time Time::operator-(const Time& t) const
{
Time diff;
int tot1, tot2;
tot1 = hours * 60 + minutes;
tot2 = t.hours * 60 + t.minutes;
diff.minutes = (tot1 - tot2) % 60;
diff.hours = (tot1 - tot2) / 60;
return diff;
}
Time Time::operator*(double mult) const
{
Time result;
long totalminutes = hours * 60 * mult + minutes * mult;
result.minutes = totalminutes % 60;
result.hours = totalminutes / 60;
return result;
}
ostream& operator<<(ostream& os, const Time& t)
{
os << t.hours << " hours " << t.minutes << " minutes";
return os;
}
//程序清单 11.12
#include <iostream>
#include "mytime3.h"
int main(void)
{
Time aida(3, 35);
Time tosca(2, 48);
Time temp;
cout << "aida and tosca: ";
cout << aida << "; " << tosca << endl;
temp = aida + tosca;
cout << "aida + tosca = " << temp << endl;
temp = aida * 1.17;
cout << "aida * 1.17 = " << temp << endl;
temp = 10.0 * tosca;
cout << "10.0 * tosca = " << temp << endl;
return 0;
}
11.4 重载运算符:作为成员函数还是非成员函数
一般来说,非成员函数应是友元函数。
- Time operator+(const Time & t) const;
- friend Time operator+(const Time& t1, const Time& t2)
- 对于成员函数版本,一个操作数通过this指针隐式传递,另一个操作数作为函数参数显式传递
- 对于友元函数版本,两个操作数都作为参数传递
11.5 再谈重载:一个矢量类
//程序清单 11.13 vector.h
#pragma once
#ifndef VECTOR_H_
#define VECTOR_H_
#include <iostream>
using namespace std;
namespace VECTOR
{
class Vector
{
public:
Vector();
~Vector();
enum Mode { RECT, POL };
Vector(double n1, double n2, Mode form = RECT);
void reset(double n1, double n2, Mode form = RECT);
double xval() const { return x; }
double yval() const { return y; }
double magval() const { return mag; }
double angval() const { return ang; }
void polar_mode();
void rect_mode();
Vector operator+(const Vector& b) const;
Vector operator-(const Vector& b) const;
Vector operator-();
Vector operator*(double mul) const;
friend Vector operator*(double mul, const Vector& a);
friend ostream& operator<<(ostream& os, const Vector& v);
//review 7
operator double() const;
private:
double x, y;
double mag, ang;
Mode mode;
void set_x();
void set_y();
void set_mag();
void set_ang();
};
}
#endif // !VECTOR_H_
//程序清单 11.14
#include <iostream>
#include <cmath>
#include "vector.h"
namespace VECTOR
{
const double Rad_to_deg = 45.0 / atan(1.0); //1弧度==?角度
void Vector::set_x()
{
x = mag * cos(ang);
}
void Vector::set_y()
{
y = mag * sin(ang);
}
void Vector::set_mag()
{
mag = sqrt(x * x + y * y);
}
void Vector::set_ang()
{
if (0 == x && 0 == y) //原点处
ang = 0;
else
ang = atan2(y, x); //结果为弧度制
}
Vector::Vector()
{
x = y = mag = ang = 0;
mode = RECT;
}
Vector::~Vector()
{
}
Vector::Vector(double n1, double n2, Mode form)
{
mode = form;
if (form == RECT) //切记==
{
x = n1;
y = n2;
set_ang();
set_mag();
}
else if (form == POL) //切记==
{
mag = n1;
ang = n2 / Rad_to_deg;
set_x();
set_y();
}
else
{
cout << "Incorrect! Vector set to 0\n";
x = y = mag = ang = 0;
mode = RECT;
}
}
void Vector::reset(double n1, double n2, Mode form)
{
mode = form;
if (form == RECT) //切记==
{
x = n1;
y = n2;
set_ang();
set_mag();
}
else if (form == POL) //切记==
{
mag = n1;
ang = n2 / Rad_to_deg; //弧度转换为角度
set_x();
set_y();
}
else
{
cout << "Incorrect! Vector set to 0\n";
x = y = mag = ang = 0;
mode = RECT;
}
}
void Vector::polar_mode()
{
mode = POL;
}
void Vector::rect_mode()
{
mode = RECT;
}
Vector Vector::operator+(const Vector& b) const
{
return Vector(x + b.x, y + b.y); //调用构造函数,创建临时对象
}
Vector Vector::operator-(const Vector& b) const
{
return Vector(x - b.x, y - b.y);
}
Vector Vector::operator-()
{
return Vector(-x, -y);
}
11.5.2 为Vector类重载算术运算符
Vector Vector::operator+(const Vector & b) const
{
Vector sum;
sum.x = x + b.x;
sum.y = y + b.y;
return sum;
}
升级:
Vector Vector::operator+(const Vector & b) const
{
Vector sum;
sum.x = x + b.x;
sum.y = y + b.y;
sum.set_ang(sum.x, sum.y);
sum.set_mag(sum.x, sum.y);
return sum;
}
升级:(调用构造函数)
Vector Vector::operator+(const Vector & b) const
{
return Vector(x + b.x, y + b.y);
}
如果方法通过计算返回一个新的类对象,可以考虑使用类构造函数完成。
11.5.4 使用Vector类模拟随机漫步
//程序清单 11.15
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "vector.h"
using namespace VECTOR;
int main(void)
{
double target;
double dstep;
Vector result(0.0, 0.0); //默认起始在原点
double direction;
srand(time(0));
//srand(time(NULL))
Vector step;
unsigned long steps = 0;
cout << "Enter target distance (q to quit): ";
while (cin >> target)
{
cout << "Enter step length: ";
if (!(cin >> dstep)) //输入0,原地不动
break;
while (result.magval() < target)
{
direction = rand() % 360; //产生0~359度之间
step.reset(dstep, direction, Vector::POL); //在main中访问Vector类成员变量POL
result = result + step; //向量和
steps++; //走多少步
}
cout << "After " << steps << " steps, the subject has the following location:\n";
cout << result << endl;
result.polar_mode(); //坐标系转换
cout << result << endl << endl;
//返回原点重新开始
steps = 0;
result.reset(0.0, 0.0);
cout << "Enter target distance (q to quit): ";
}
cout << "Bye!\n";
//输错清空
cin.clear();
while (cin.get() != '\n')
continue;
return 0;
}
11.6 类的自动转换和强制类型转换
//程序清单 11.16 stonewt.h
#pragma once
#ifndef STONEWT_H_
#define STONEWT_H_
#include <iostream>
using namespace std;
class Stonewt
{
public:
Stonewt();
~Stonewt();
Stonewt(double lbs); //自动隐式转换
//explicit Stonewt(double lbs); //强制显式转换
Stonewt(int stn, double lbs);
void show_lbs() const;
void show_stn() const;
private:
enum {Lbs_per_stn = 14};
//static const int Lbs_per_stn = 14;
int stone;
double pds_left;
double pounds;
};
#endif // !STONEWT_H_
//程序清单 11.17
#include "stonewt.h"
Stonewt::Stonewt()
{
stone = pounds = pds_left = 0;
}
Stonewt::~Stonewt()
{
}
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
void Stonewt::show_lbs() const
{
cout << pounds << " pounds\n";
}
void Stonewt::show_stn() const
{
cout << stone << " stone, " << pds_left << " pounds\n";
}
下面的构造函数用于将double类型的值转换为Stonewt类型:
Stonewt(double lbs);
也就是说,可以编写这样的代码:
Stonewt myCat;
myCat = 19.6;
程序使用构造函数Stonewt(double)创建临时的Stonewt对象,并将19.6作为初始化值。
随后逐成员赋值将该临时对象的内容赋值到myCat中。此过程为隐式转换,自动进行。
只有接收一个参数的构造函数才能作为转换函数,如果有两个参数,给第二个参数提供了默认值,也可以。
C++新增explicit关键字,关闭这种自动隐式转换,允许显式强制类型转换。
explicit Stonewt(double lbs);
Stonewt myCat;
myCat = 19.6; //错误
myCat = Stonewt(19.6);
myCat = (Stonewt)19.6;
//程序清单 11.18
#include <iostream>
#include "stonewt.h"
int main(void)
{
Stonewt incognito = 275; //使用构造函数初始化
//Stonewt incognito(275);
//Stonewt incognito = Stonewt(275);
Stonewt wolfe(285.7);
Stonewt taft(21, 8);
cout << "The celebrity weighed: ";
incognito.show_stn();
incognito.show_lbs();
cout << "The detective weighed: ";
wolfe.show_stn();
wolfe.show_lbs();
cout << "The president weighed: ";
taft.show_stn();
taft.show_lbs();
return 0;
}
11.6.1 转换函数
程序清单11.18将数字转换为Stonewt对象。
转换函数可以将Stonewt对象转换为某种类型。
转换函数是用户定义的强制类型转换。
例如:operator double ();
- 转换函数必须是类方法(类成员函数); 需要通过类对象调用
- 转换函数不能指定返回类型; double指出来要转换的类型,所以不需要
- 转换函数不能有参数。 类对象告知要转换的值,所以不需要
//程序清单 11.19 Stonewt1.h
#pragma once
#ifndef STONEWT1_H_
#define STONEWT1_H_
#include <iostream>
using namespace std;
class Stonewt
{
public:
Stonewt();
~Stonewt();
Stonewt(double lbs); //自动隐式转换
//explicit Stonewt(double lbs); //强制显式转换
Stonewt(int stn, double lbs);
void show_lbs() const;
void show_stn() const;
operator int() const;
operator double() const;
//强制显式转换
//explicit operator int() const;
//explicit operator double() const;
//review 1
Stonewt operator*(double mult) const;
//review 4
friend Stonewt operator*(double mult, const Stonewt& s);
private:
//enum { Lbs_per_stn = 14 };
static const int Lbs_per_stn = 14;
int stone;
double pds_left;
double pounds;
};
#endif // !STONEWT1_H_
//程序清单 11.20
#include "stonewt1.h"
Stonewt::Stonewt()
{
stone = pounds = pds_left = 0;
}
Stonewt::~Stonewt()
{
}
Stonewt::Stonewt(double lbs)
{
stone = int(lbs) / Lbs_per_stn;
pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs);
pounds = lbs;
}
Stonewt::Stonewt(int stn, double lbs)
{
stone = stn;
pds_left = lbs;
pounds = stn * Lbs_per_stn + lbs;
}
void Stonewt::show_lbs() const
{
cout << pounds << " pounds\n";
}
void Stonewt::show_stn() const
{
cout << stone << " stone, " << pds_left << " pounds\n";
}
Stonewt::operator int() const
{
return int(pounds + 0.5);
}
Stonewt::operator double() const
{
return pounds;
}
//review 1
Stonewt Stonewt::operator*(double mult) const
{
return Stonewt(pounds * mult);
}
//review 4
Stonewt operator*(double mult, const Stonewt& s)
{
return(mult * s.stone);
}
//程序清单 11.21
#include <iostream>
#include "stonewt1.h"
int main(void)
{
Stonewt poppins(9, 2.8);
double p_wt = poppins; //隐式转换
cout << "Convert to double => ";
cout << "Poppins: " << p_wt << " pounds.\n";
cout << "Covert to int => ";
cout << "Popins: " << int(poppins) << " pounds.\n"; //显式强制转换
return 0;
}
应谨慎使用隐式转换,最好使用显式转换。