• C++类的运算符重载.md


    11.8 类的运算符重载

    运算符重载是一种形式的C++多态

    运算符重载将充值该的概念扩展到运算符上,允许赋予C++运算符多种含义。

    C++允许将运算符重载扩展到用户定义类型,例如,允许使用+将两个对象相加。

    11.8.1 操作符重载定义

    要重载运算符,需使用被称为运算符函数的特殊函数形式。运算符函数的格式如下:

    operator op(argument-list)
    
    • 1

    例如,operator+()重载+运算符,operator*()重载*运算符。op必须是有效的C++运算符,不能虚构一个新的符号。例如,不能有operator@()这样的函数,因为C++中没有@运算符。然而,operatoe函数将重载[]运算符,因为[]是数组索引运算符。

    11.8.2 调用重载的操作符

    11.8.2.1 使用重载的操作符(常用)

    假设有一个Salesperson类,并为它定义了一个operator+()成员函数,以重载+运算符,以便能够将两个Salesperson对象的销售额相加,如果district2, sid, and sara都是Salesperson类的对象,便可以编写这样的等式。

    district2 = sid + sara;
    
    • 1

    编译器发现,操作数是Salesperson类对象,因此使用相应的运算符函数替换上述运算符。

    district2 = sid.operator+(sara);
    
    • 1

    该函数将隐式地调用sid(因为它调用了方法),而显式地使用sara对象(因为他被作为参数传递)

    11.8.2.2 使用operatorop()函数名
    //比如这个加法
    total = morefixing.operator+(total);
    
    • 1
    • 2
    11.8.2.3 是否可以实现连加(or 连减、连乘…)
    t4 = t1 + t2 + t3; // valid?
    t4 = t1.operator+(t2 + t3); // valid?
    t4 = t1.operator+(t2.operator+(t3)); // valid? YES
    
    • 1
    • 2
    • 3

    11.8.3 重载限制

    重载的运算符(有些例外情况)不必是成员函数,但必须至少有一个操作数是用户定义的类型,以下是函数重载地限制:

    1.重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。因此,不能将减法运算符(-)重载为计算两个double值的和,而是他们的差。虽然这种限制会对创造性有所影响,但可以确保程序正常运行。

    2.使用运算符时不能违反运算符原来的句法规则。例如,不能将求模运算符(%)重载成为一个操作数。

    int x;
    Time shiva;
    % x; // invalid for modulus operator
    % shiva; // invalid for overloaded operator
    
    • 1
    • 2
    • 3
    • 4

    3.不能修改运算符的优先级。因此,如果将加号运算符 重载为两个类相加,则新的运算符与原来的加号具有相同的优先级。

    4.不能创建新运算符。例如,不能定义operator**()函数来表示求幂。

    5.不能重载以下运算符:

    在这里插入图片描述

    6.表11.1中的大多数运算符都可以通过成员或非成员函数进行重载,但下面的运算符只能通过成员函数进行重载:

    在这里插入图片描述

    7.本章不介绍这里列出的所有运算符,但附录E对本书正文中没有介绍的运算符进行了总结。

    11.8.4 举例

    11.8.4.1 源代码
    #pragma once
    // mytime0.h -- Time class before operator overloading
    #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;
    	Time operator-(const Time& t) const;
    	Time operator*(double n) const;
    	void Show() const;
    };
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    // mytime0.cpp -- implementing Time methods
    #include 
    #include "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;
    }
    Time Time::operator-(const Time& t) const
    {
    	Time diff;
    	int tot1, tot2;
    	tot1 = t.minutes + 60 * t.hours;
    	tot2 = minutes + 60 * hours;
    	diff.minutes = (tot2 - tot1) % 60;
    	diff.hours = (tot2 - tot1) / 60;
    	return diff;
    }
    Time Time::operator*(double mult) const
    {
    	Time result;
    	long totalminutes = hours * mult * 60 + minutes * mult;
    	result.hours = totalminutes / 60;
    	result.minutes = totalminutes % 60;
    	return result;
    }
    void Time::Show() const
    {
    	std::cout << hours << " hours, " << minutes << " minutes";
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    // usetime0.cpp -- using the first draft of the Time class
    // compile usetime0.cpp and mytime0.cpp together
    #include 
    #include "mytime0.h"
    
    int main()
    {
    	using std::cout;
    	using std::endl;
    	Time weeding(4, 35);
    	Time waxing(2, 47);
    	Time total;
    	Time diff;
    	Time adjusted;
    	cout << "weeding time = ";
    	weeding.Show();
    	cout << endl;
    	cout << "waxing time = ";
    	waxing.Show();
    	cout << endl;
    	cout << "total work time = ";
    	total = weeding + waxing; // use operator+()
    	total.Show();
    	cout << endl;
    	diff = weeding - waxing; // use operator-()
    	cout << "weeding time - waxing time = ";
    	diff.Show();
    	cout << endl;
    	adjusted = total * 1.5; // use operator*()
    	cout << "adjusted work time = ";
    	adjusted.Show();
    	cout << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    11.8.4.2 运行结果
    weeding time = 4 hours, 35 minutes
    waxing time = 2 hours, 47 minutes
    total work time = 7 hours, 22 minutes
    weeding time - waxing time = 1 hours, 48 minutes
    adjusted work time = 11 hours, 3 minutes
    
    D:\Prj\C++\Function_OverLoading\Debug\Function_OverLoading.exe (进程 3524)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    11.8.4.3 注意事项

    不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。

    11.8.5 重载运算符: 成员函数重载VS非成员函数重载

    大多数运算符都可以通过成员或非成员函数来重载,非成员函数是友元函数,因此它可以直接访问对象的所有私有数据。

    Time operator+(const Time & t) const; // member version
    // nonmember version
    friend Time operator+(const Time & t1, const Time & t2);
    
    • 1
    • 2
    • 3

    加法运算符需要两个操作数。对于成员函数版本来说,一个操作数通过this指针隐式地传递,另一个操作数作为函数参数显式地传递;对于友元版本来说,两个操作数都为参数来传递。

    T1 = T2 + T3;
    T1 = T2.operator+(T3); // member function
    T1 = operator+(T2, T3); // nonmember function
    
    • 1
    • 2
    • 3

    记住,在定义运算符时,必须选择其中的一种格式,而不能同时选择这两种格式。因为这两种格式都与同一个表达式匹配,同时定义这两种格式将被视为二义性错误,导致编译错误。

    那么哪种格式最好呢?对于某些运算符来说(如前所述),成员函数是唯一合法的选择。在其他情况下,这两种格式没有太大的区别。有时,根据类设计,使用非成员函数版本可能更好(尤其是为类定义类型转换时)

    11.8.6 矢量类操作符重载

    矢量(vector)是工程和物理中使用的一个术语,它是一个有大小和方向的量。

    11.8.6.1 多种表示方式和类

    可以用不同但等价的方式表示一个实体。

    类非常适于在一个对象中表示实体的不同方面。首先在一个对象中存储多种表示方式;然后,编写这样的类函数,以便给一种表示方式赋值时,将自动给其他方式赋值。

    11.8.6.2 为Vector类重载算数运算符

    如果方法通过计算得到一个新的类对象,则应考虑是否可以使用类构造函数来完成这种工作。这样做不仅可以避免麻烦,而且可以确保新的对象时按照正确的方式创建的。

    11.8.6.3 对已重载的运算符进行重载

    在C++中,-运算符有两种含义;首先,使用两个操作数,它是减法运算符。减法运算是一个二元运算符,因为它有两个操作数。其次,使用一个操作数时(如-x),它是负号运算符。这种形式被称为一元运算符。即只有一个操作数。对于矢量来说,这两种操作都是有意义的,因此Vector类要有这两种操作。

    因为运算符重载是通过函数来实现的,所以只要运算符函数的参数不同,使用的运算符数量与相应的内置C++运算符相同,就可以多次重载同一个运算符。

    11.8.6.4 对实现的说明

    Vector对象可以只存储x和y分量,而返回矢量长度的magval()方法可以根据x和y的值来计算出长度,而不是查找对象中存储的这个值。这种方法改变了实现,但用户接口不变。将接口与实现分离是OOP的目标之一,这样允许对实现进行调整,而无需修改使用这个类的程序中的代码。

    一种实现是只存储直角坐标,另一种实现是存储直角坐标和极坐标。这两种实现各有利弊,存储数据意味着对象将占据更多的内存,每次Vector对象被修改时,都需要更新直角坐标和极坐标表示;但查找数据的速度比较快。如果应用程序经常需要访问矢量的这两种表示,则第二种实现比较合适;如果只是偶尔使用极坐标,则另一种更好。可以在一个程序中使用一种实现,而在另一个程序中使用另一种实现,但他们的用户接口相同。

    11.8.6.5 一个实例–使用Vector类模拟随机漫步

    将一个人领到一个街灯柱下,这个人开始走动,但每一步的方向都是随机的。提出的问题是:这个人走到离灯柱50英尺处需要多少步,从矢量的角度看,这相当于不断将方向随机的矢量相加,直到长度超过50英尺。

    11.8.6.5.1 code
    #pragma once
    //添加命名空间是为了复习之前命名空间的内容
    // vect.h -- Vector class with <<, mode state
    #ifndef VECTOR_H_
    #define VECTOR_H_
    #include 
    namespace VECTOR
    {
    	class Vector
    	{
    	public:
    		enum Mode { RECT, POL };
    		// RECT for rectangular, POL for Polar modes
    	private:
    		double x; // horizontal value
    		double y; // vertical value
    		double mag; // length of vector
    		double ang; // direction of vector in degrees
    		Mode mode; // RECT or POL---Such a member is termed a state member because it describes the state an object is in.
    		// private methods for setting values
    		void set_mag();
    		void set_ang();
    		void set_x();
    		void set_y();
    	public:
    		Vector();
    		Vector(double n1, double n2, Mode form = RECT);
    		void reset(double n1, double n2, Mode form = RECT);
    		~Vector();
    		double xval() const { return x; } // report x value
    		double yval() const { return y; } // report y value
    		double magval() const { return mag; } // report magnitude
    		double angval() const { return ang; } // report angle
    		void polar_mode(); // set mode to POL
    		void rect_mode(); // set mode to RECT
    		// operator overloading
    		Vector operator+(const Vector& b) const;
    		Vector operator-(const Vector& b) const;
    		Vector operator-() const;
    		Vector operator*(double n) const;
    		// friends
    		friend Vector operator*(double n, const Vector& a);
    		friend std::ostream& operator<<(std::ostream& os, const Vector& v);
    	};
    } // end namespace VECTOR
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    // vect.cpp -- methods for the Vector class
    #include 
    #include "vect.h" // includes 
    using std::sqrt;
    using std::sin;
    using std::cos;
    using std::atan;
    using std::atan2;
    using std::cout;
    namespace VECTOR
    {
    	// compute degrees in one radian
    	const double Rad_to_deg = 45.0 / atan(1.0);
    	// should be about 57.2957795130823
    	// private methods
    	// calculates magnitude from x and y
    	void Vector::set_mag()
    	{
    		mag = sqrt(x * x + y * y);
    	}
    	void Vector::set_ang()
    	{
    		if (x == 0.0 && y == 0.0)
    			ang = 0.0;
    		else
    			ang = atan2(y, x);
    	}
    	// set x from polar coordinate
    	void Vector::set_x()
    	{
    		x = mag * cos(ang);
    	}
    	// set y from polar coordinate
    	void Vector::set_y()
    	{
    		y = mag * sin(ang);
    	}
    	// public methods
    	Vector::Vector() // default constructor
    	{
    		x = y = mag = ang = 0.0;
    		mode = RECT;
    	}
    	// construct vector from rectangular coordinates if form is r
    	// (the default) or else from polar coordinates if form is p
    	Vector::Vector(double n1, double n2, Mode form)
    	{
    		mode = form;
    		if (form == RECT)
    		{
    			x = n1;
    			y = n2;
    			set_mag();
    			set_ang();
    		}
    		else if (form == POL)
    		{
    			mag = n1;
    			ang = n2 / Rad_to_deg;
    			set_x();
    			set_y();
    		}
    		else
    		{
    			cout << "Incorrect 3rd argument to Vector() -- ";
    			cout << "vector set to 0\n";
    			x = y = mag = ang = 0.0;
    			mode = RECT;
    		}
    	}
    	// reset vector from rectangular coordinates if form is
    	// RECT (the default) or else from polar coordinates if
    	// form is POL
    	void Vector::reset(double n1, double n2, Mode form)
    	{
    		mode = form;
    		if (form == RECT)
    		{
    			x = n1;
    			y = n2;
    			set_mag();
    			set_ang();
    		}
    		else if (form == POL)
    		{
    			mag = n1;
    			ang = n2 / Rad_to_deg;
    			set_x();
    			set_y();
    		}
    		else
    		{
    			cout << "Incorrect 3rd argument to Vector() -- ";
    			cout << "vector set to 0\n";
    			x = y = mag = ang = 0.0;
    			mode = RECT;
    		}
    	}
    	Vector::~Vector() // destructor
    	{
    	}
    	void Vector::polar_mode() // set to polar mode
    	{
    		mode = POL;
    	}
    	void Vector::rect_mode() // set to rectangular mode
    	{
    		mode = RECT;
    	}
    	// operator overloading
    	// add two Vectors
    	Vector Vector::operator+(const Vector& b) const
    	{
    		return Vector(x + b.x, y + b.y);
    	}
    	// subtract Vector b from a
    	Vector Vector::operator-(const Vector& b) const
    	{
    		return Vector(x - b.x, y - b.y);
    	}
    	// reverse sign of Vector
    	Vector Vector::operator-() const
    	{
    		return Vector(-x, -y);
    	}
    	// multiply vector by n
    	Vector Vector::operator*(double n) const
    	{
    		return Vector(n * x, n * y);
    	}
    	// friend methods
    	// multiply n by Vector a
    	Vector operator*(double n, const Vector& a)
    	{
    		return a * n;//这里就会调用a的成员函数*
    	}
    	// display rectangular coordinates if mode is RECT,
    	// else display polar coordinates if mode is POL
    	std::ostream& operator<<(std::ostream& os, const Vector& v)
    	{
    		if (v.mode == Vector::RECT)
    			os << "(x,y) = (" << v.x << ", " << v.y << ")";
    		else if (v.mode == Vector::POL)
    		{
    			os << "(m,a) = (" << v.mag << ", "
    				<< v.ang * Rad_to_deg << ")";
    		}
    		else
    			os << "Vector object mode is invalid";
    		return os;
    	}
    } // end namespace VECTOR
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    // randwalk.cpp -- using the Vector class
    // compile with the vect.cpp file
    #include 
    #include  // rand(), srand() prototypes
    #include  // time() prototype
    #include "vect.h"
    int main()
    {
    	using namespace std;
    	using VECTOR::Vector;
    	srand(time(0)); // seed random-number generator
    	double direction;
    	Vector step;
    	Vector result(0.0, 0.0);
    	unsigned long steps = 0;
    	double target;
    	double dstep;
    	cout << "Enter target distance (q to quit): ";
    	while (cin >> target)
    	{
    		cout << "Enter step length: ";
    		if (!(cin >> dstep))
    			break;
    		while (result.magval() < target)
    		{
    			direction = rand() % 360;//rand()产生随机数的范围为0~RAND_MAX,RAND_MAX值最小为32767,最大为2147483647
    			step.reset(dstep, direction, Vector::POL);
    			result = result + step;
    			steps++;
    		}
    		cout << "After " << steps << " steps, the subject "
    			"has the following location:\n";
    		cout << result << endl;
    		result.polar_mode();
    		cout << " or\n" << result << endl;
    		cout << "Average outward distance per step = "
    			<< result.magval() / steps << 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    这个程序用一个变量来表示位置(一个矢量),并报告到达只当距离处(用两种格式表示)所需的步数。

    11.8.6.5.2 程序笔记

    标准ANSI C库(C++也有)中有一个rand()函数,它返回一个从0到某个值之间的随机整数。上述程序使用求模操作数来获得一个0~359的角度值。rand()函数将一种算法用于一个初始种子值来获得随机数,该随机数将用作下一次函数调用的种子。这些数实际上是伪随机数,因为10次连续的调用通常将生成10个同样的随机数。然而,srand()函数允许覆盖默认的种子值,重新启动从某一个随机数序列,该程序使用time(0)的返回值来设置种子。time(0)函数返回当前时间,通常为从某一个日期开始的秒数(更广义的,time()接受time_t变量的地址,将时间放到该变量中,并返回它。将0用作地址参数,可以省略time_t变量声明)。因此,下面的语句在每次运行程序时,都将设置不同的种子,使随机输出看上去更为随机。

    srand(time(0));
    
    • 1

    头文件cstdlib(以前为stdlib.h)包含了srand()和rand()的原型,而ctime(以前是time.h)包含了time()的原型。C++11使用头文件random中的函数提供了更强大的随机数支持。

  • 相关阅读:
    利用kubeadmin快速搭建kubenates集群
    笔记_前端基础试题-面试前的准备
    MySQL---表的增删改查(基础)
    springboot二手交易平台 毕业设计-附源码290915
    【STM32】FSMC接口的复用和非复用
    俄罗斯方块游戏开发教程5:形状碰撞检测(下)
    Python之并发编程
    推荐几个开源类库,超好用,早用找下班
    VMware安装Centos
    字节、字、双字 关系
  • 原文地址:https://blog.csdn.net/weixin_44410704/article/details/127934810