C++_primer_plus学习笔记 第11章 使用类

本章内容包括:

  • 运算符重载
  • 友元函数
  • 重载<<运算符
  • 状态成员
  • 使用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 重载限制

  1. 重载后的运算符至少有一个操作数是用户定义的类型,防止将标准类型重载运算符。
  2. 重载运算符不能违反原来的句法规则,不能修改优先级。
  3. 不能创建新的运算符。
  4. 不能重载的运算符:sizeof、成员运算符(.)、成员指针运算符(.*)、作用域解析运算符(::)、条件运算符(?:)
  5. 只能通过成员函数进行重载:赋值运算符(=)、函数调用运算符()、下标运算符[ ]、指针访问类成员运算符(->)        

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;
}

应谨慎使用隐式转换,最好使用显式转换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值