C++ 重载运算符 addition +, subtraction - and multiplication *
- 1. Operator Overloading (运算符重载)
- 2. Developing an Operator Overloading Example
- 2.1. Adding an Addition Operator (添加加法运算符)
- 2.2. Overloading Restrictions (重载限制)
- 2.3. 重载运算符 `-` 和 `*`
- References
Two special member functions, the constructor and the destructor, that manage creating and discarding objects made to a class specification.
两个特殊的成员函数 (构造函数和析构函数),其作用是管理类对象的创建和删除。
As Bjarne Stroustrup, the creator of C++, suggested at a C++ conference for professional programmers:“Ease yourself into the language. Don’t feel you have to use all of the features, and don’t try to use them all on the first day.”
不要觉得必须使用所有的特性,不要在第一次学习时就试图使用所有的特性。
1. Operator Overloading (运算符重载)
Operator overloading is an example of C++ polymorphism. C++ enables you to define several functions that have the same name, provided that they have different signatures (argument lists).That is called function overloading, or functional polymorphism. Its purpose is to let you use the same function name for the same basic operation, even though you apply the operation to different data types. Operator overloading extends the overloading concept to operators, letting you assign multiple meanings to C++ operators.
运算符重载是一种形式的 C++ 多态。用户能够定义多个名称相同但特征标 (参数列表) 不同的函数的,这被称为函数重载或函数多态,旨在让您能够用同名的函数来完成相同的基本操作,即使这种操作被用于不同的数据类型。运算符重载将重载的概念扩展到运算符上,允许赋予 C++ 运算符多种含义。
C++ lets you extend operator overloading to user-defined types.
C++ 允许将运算符重载扩展到用户定义的类型,例如,允许使用 +
将两个对象相加。
To overload an operator, you use a special function form called an operator function
.
要重载运算符,需使用被称为运算符函数的特殊函数形式。
An operator function
has the following form, where OP
is the symbol for the operator being overloaded:
operatorOP(argument-list)
For example, operator+()
overloads the +
operator and operator*()
overloads the *
operator. The OP
has to be a valid C++ operator; you can’t just make up a new symbol. For example, you can’t have an operator@()
function because C++ has no @
operator. But the operator[]()
function would overload the []
operator because []
is the array-indexing operator. Suppose, for example, that you have a SalesPerson
class for which you define an operator+()
member function to overload the +
operator so that it adds sales figures of one SalesPerson
object to another. Then, if district2
, yong
, and qiang
are all objects of the SalesPerson
class, you can write this equation:
例如,operator+()
重载 +
运算符,operator*()
重载 *
运算符。OP
必须是有效的 C++ 运算符,不能虚构一个新的符号。例如,不能有 operator@()
这样的函数,因为 C++ 中没有 @
运算符。然而,operator[]()
函数将重载 []
运算符,因为 []
是数组索引运算符。例如,假设有一个 SalesPerson
类,并为它定义了一个 operator+()
成员函数,以重载 +
运算符,以便能够将两个 SalesPerson
对象的销售额相加,则如果 district2
, yong
, and qiang
都是 SalesPerson
类对象,便可以编写这样的等式:
district2 = yong + qiang;
The compiler, recognizing the operands as belonging to the Salesperson
class, replaces the operator with the corresponding operator function:
district2 = yong.operator+(qiang);
The function then uses the yong
object implicitly (because it invoked the method) and the qiang
object explicitly (because it’s passed as an argument) to calculate the sum, which it then returns. Of course, the nice part is that you can use the nifty +
operator notation instead of the clunky function notation.
然后该函数将隐式地使用 yong
(因为它调用了方法),而显式地使用 qiang
对象 (因为它被作为参数传递),来计算总和,并返回这个值。当然最重要的是,可以使用简便的 +
运算符表示法,而不必使用笨拙的函数表示法。
2. Developing an Operator Overloading Example
Now let’s generalize that to a Time
class, using a method to handle addition. Let’s begin with an ordinary method, called Sum()
, and then see how to convert it to an overloaded operator.
首先使用一个名为 Sum()
的常规方法,然后介绍如何将其转换为重载运算符。
- time.h
// Time class before operator overloading
#ifndef TIME_H_
#define TIME_H_
class Time {
private:
int hours;
int minutes;
public:
Time();
Time(const int h, const int m = 0);
~Time();
void AddMinutes(const int m);
void AddHours(const int h);
void Reset(const int h = 0, const int m = 0);
const Time Sum(const Time & time) const;
void Show() const;
};
#endif
- time.cpp
#include <iostream>
#include "time.h"
Time::Time() {
std::cout << "Time::Time()" << std::endl;
hours = minutes = 0;
}
Time::Time(const int h, const int m) {
std::cout << "Time::Time(const int h, const int m): h=" << h << ", m=" << m << std::endl;
hours = h;
minutes = m;
}
Time::~Time() {
std::cout << "Time::~Time(): hours=" << hours << ", minutes=" << minutes << std::endl;
}
void Time::AddMinutes(const int m) {
minutes += m;
hours += minutes / 60;
minutes %= 60;
}
void Time::AddHours(const int h) {
hours += h;
}
void Time::Reset(const int h, const int m) {
hours = h;
minutes = m;
}
const Time Time::Sum(const Time & time) const {
std::cout << "Time::Sum(const Time & time)" << std::endl;
Time sum;
sum.minutes = minutes + time.minutes;
sum.hours = hours + time.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
void Time::Show() const {
std::cout << hours << " hours, " << minutes << " minutes";
}
- sample.cpp
#include <iostream>
#include "time.h"
int main() {
{
Time planning;
Time coding(2, 40);
Time fixing(5, 55);
Time total;
std::cout << "planning time = ";
planning.Show();
std::cout << std::endl;
std::cout << "coding time = ";
coding.Show();
std::cout << std::endl;
std::cout << "fixing time = ";
fixing.Show();
std::cout << std::endl;
total = coding.Sum(fixing);
std::cout << "coding.Sum(fixing) = ";
total.Show();
std::cout << std::endl;
}
return 0;
}
Time::Time()
Time::Time(const int h, const int m): h=2, m=40
Time::Time(const int h, const int m): h=5, m=55
Time::Time()
planning time = 0 hours, 0 minutes
coding time = 2 hours, 40 minutes
fixing time = 5 hours, 55 minutes
Time::Sum(const Time & time)
Time::Time()
Time::~Time(): hours=8, minutes=35
Time::~Time(): hours=8, minutes=35
coding.Sum(fixing) = 8 hours, 35 minutes
Time::~Time(): hours=8, minutes=35
Time::~Time(): hours=5, minutes=55
Time::~Time(): hours=2, minutes=40
Time::~Time(): hours=0, minutes=0
请按任意键继续. . .
Note that the argument is a reference but that the return type is not a reference. The reason for making the argument a reference is efficiency. The code would produce the same results if the Time
object were passed by value, but it’s usually faster and more memory-efficient to just pass a reference.
Sum()
函数的代码,参数是引用,但返回类型却不是引用。将参数声明为引用的目的是为了提高效率。如果按值传递 Time
对象,代码的功能相同,但传递引用,速度将更快,使用的内存将更少。
However, the return value can not be a reference. The reason is that the function creates a new Time
object (sum
) that represents the sum of the other two Time
objects. Returning the object, as this code does, creates a copy of the object that the calling function can use. If the return type were Time &
, however, the reference would be to the sum
object. But the sum
object is a local variable and is destroyed when the function terminates, so the reference would be a reference to a non-existent object. Using a Time
return type, however, means the program constructs a copy of sum
before destroying it, and the calling function gets the copy.
然而,返回值不能是引用。因为函数将创建一个新的 Time
对象 (sum
),来表示另外两个 Time
对象的和。返回对象将创建对象的副本,而调用函数可以使用它。然而,如果返回类型为 Time &
,则引用的将是 sum
对象。但由于 sum
对象是局部变量,在函数结束时将被删除,因此引用将指向一个不存在的对象。使用返回类型 Time
意味着程序将在删除 sum
之前构造它的拷贝,调用函数将得到该拷贝。
Don’t return a reference to a local variable or another temporary object. When the function terminates and the local variable or temporary object disappears, the reference becomes a reference to non-existent data.
不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据。
2.1. Adding an Addition Operator (添加加法运算符)
You just change the name of Sum()
to the odder-looking name operator+()
. That’s right: You just append the operator symbol (+
, in this case) to the end of operator and use the result as a method name. This is one place where you can use a character other than a letter, a digit, or an underscore in an identifier name.
将 Time
类转换为重载的加法运算符很容易,只要将 Sum()
的名称改为 operator+()
即可。只要把运算符放到 operator
的后面,并将结果用作方法名即可。在这里,可以在标识符中使用字母、数字或下划线之外的其他字符。
- time.h
// Time class before operator overloading
#ifndef TIME_H_
#define TIME_H_
class Time {
private:
int hours;
int minutes;
public:
Time();
Time(const int h, const int m = 0);
~Time();
void AddMinutes(const int m);
void AddHours(const int h);
void Reset(const int h = 0, const int m = 0);
Time operator+(const Time & time) const;
void Show() const;
};
#endif
- time.cpp
#include <iostream>
#include "time.h"
Time::Time() {
std::cout << "Time::Time()" << std::endl;
hours = minutes = 0;
}
Time::Time(const int h, const int m) {
std::cout << "Time::Time(const int h, const int m): h=" << h << ", m=" << m << std::endl;
hours = h;
minutes = m;
}
Time::~Time() {
std::cout << "Time::~Time(): hours=" << hours << ", minutes=" << minutes << std::endl;
}
void Time::AddMinutes(const int m) {
minutes += m;
hours += minutes / 60;
minutes %= 60;
}
void Time::AddHours(const int h) {
hours += h;
}
void Time::Reset(const int h, const int m) {
hours = h;
minutes = m;
}
Time Time::operator+(const Time & time) const {
std::cout << "Time::operator+(const Time & time)" << std::endl;
Time sum;
sum.minutes = minutes + time.minutes;
sum.hours = hours + time.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
void Time::Show() const {
std::cout << hours << " hours, " << minutes << " minutes";
}
- sample.cpp
#include <iostream>
#include "time.h"
int main() {
{
Time planning;
Time coding(2, 40);
Time fixing(5, 55);
Time total;
std::cout << "planning time = ";
planning.Show();
std::cout << std::endl;
std::cout << "coding time = ";
coding.Show();
std::cout << std::endl;
std::cout << "fixing time = ";
fixing.Show();
std::cout << std::endl;
total = coding + fixing;
// operator notation
std::cout << "coding + fixing = ";
total.Show();
std::cout << std::endl;
Time more_fixing(3, 28);
std::cout << "more fixing time = ";
more_fixing.Show();
std::cout << std::endl;
total = more_fixing.operator+(total);
// function notation
std::cout << "more_fixing.operator+(total) = ";
total.Show();
std::cout << std::endl;
}
return 0;
}
Time::Time()
Time::Time(const int h, const int m): h=2, m=40
Time::Time(const int h, const int m): h=5, m=55
Time::Time()
planning time = 0 hours, 0 minutes
coding time = 2 hours, 40 minutes
fixing time = 5 hours, 55 minutes
Time::operator+(const Time & time)
Time::Time()
Time::~Time(): hours=8, minutes=35
Time::~Time(): hours=8, minutes=35
coding + fixing = 8 hours, 35 minutes
Time::Time(const int h, const int m): h=3, m=28
more fixing time = 3 hours, 28 minutes
Time::operator+(const Time & time)
Time::Time()
Time::~Time(): hours=12, minutes=3
Time::~Time(): hours=12, minutes=3
more_fixing.operator+(total) = 12 hours, 3 minutes
Time::~Time(): hours=3, minutes=28
Time::~Time(): hours=12, minutes=3
Time::~Time(): hours=5, minutes=55
Time::~Time(): hours=2, minutes=40
Time::~Time(): hours=0, minutes=0
请按任意键继续. . .
Like Sum()
, operator+()
is invoked by a Time
object, takes a second Time
object as an argument, and returns a Time
object. Thus, you can invoke the operator+()
method by using the same syntax that Sum()
uses.
和 Sum()
一样,operator+()
也是由 Time
对象调用的,它将第二个 Time
对象作为参数,并返回一个 Time
对象。因此,可以像调用 Sum()
那样来调用 operator+()
方法。
total = coding.operator+(fixing); // function notation
但将该方法命令为 operator+()
后,也可以使用运算符表示法 (operator notation):
total = coding + fixing; // operator notation
Either notation invokes the operator+()
method. Note that with the operator notation, the object to the left of the operator is the invoking object, and the object to the right is the one passed as an argument.
这两种表示法都将调用 operator+()
方法。注意,在运算符表示法中,运算符左侧的对象是调用对象,运算符右边的对象是作为参数被传递的对象。
In short, the name of the operator+()
function allows it to be invoked by using either function notation or operator notation. The compiler uses the operand types to figure out what to do.
operator+()
函数的名称使得可以使用函数表示法或运算符表示法来调用它,编译器将根据操作数的类型来确定如何做。
int a, b, c;
Time A, B, C;
c = a + b; // use int addition
C = A + B; // use addition as defined for Time objects
t4 = t1 + t2 + t3; // valid?
Because addition is a left-to-right operator, the statement is first translated to this.
由于 +
是从左向右结合的运算符,因此上述语句首先被转换成下面这样:
t4 = t1.operator+(t2 + t3);
Then the function argument is itself translated to a function call, giving the following.
然后, 函数参数本身被转换成一个函数调用:
t4 = t1.operator+(t2.operator+(t3));
The function call t2.operator+(t3)
returns a Time
object that represents the sum of t2
and t3
. This object then becomes the object of the t1.operator+()
function call, and that call returns the sum of t1
and the Time
object that represents the sum of t2
and t3
. In short, the final return value is the sum of t1
, t2
, and t3
, just as desired.
函数调用 t2.operator+(t3)
返回一个 Time
对象,表示 t2
和 t3
的和。该对象成为函数调用 t1.operator+()
的参数,该调用返回 t1
与表示 t2
和 t3
之和的 Time
对象的和。最后的返回值为 t1
、t2
和 t3
之和,这正是我们期望的。
2.2. Overloading Restrictions (重载限制)
Overloaded operators don’t necessarily have to be member functions. However, at least one of the operands has to be a user-defined type.
重载的运算符不必是成员函数,但必须至少有一个操作数是用户定义的类型。
-
The overloaded operator must have at least one operand that is a user-defined type. This prevents you from overloading operators for the standard types. Thus, you can’t redefine the minus operator (
-
) so that it yields the sum of two double values instead of their difference. This restriction preserves program sanity, although it may hinder creative accounting.
重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。因此,不能将减法运算符-
重载为计算两个double
值的和,而不是它们的差。虽然这种限制将对创造性有所影响,但可以确保程序正常运行。 -
You can’t use an operator in a manner that violates the syntax rules for the original operator. For example, you can’t overload the modulus operator (
%
) so that it can be used with a single operand.
使用运算符时不能违反运算符原来的句法规则。例如,不能将求模运算符%
重载成使用一个操作数。 -
Similarly, you can’t alter operator precedence. So if you overload the addition operator to let you add two classes, the new operator has the same precedence as ordinary addition.
同样,不能修改运算符的优先级。因此,如果将加号运算符重载成将两个类相加,则新的运算符与原来的加号具有相同的优先级。 -
You can’t create new operator symbols. For example, you can’t define an
operator**()
function to denote exponentiation.
不能创建新运算符。例如,不能定义operator**()
函数来表示求幂。 -
不能重载下面的运算符:
Operator | Description |
---|---|
sizeof | The sizeof operator |
. | The membership operator |
.* | The pointer-to-member operator |
:: | The scope-resolution operator |
?: | The conditional operator |
typeid | An RTTI operator |
const_cast | A type cast operator |
dynamic_cast | A type cast operator |
reinterpret_cast | A type cast operator |
static_cast | A type cast operator |
Most of the operators can be overloaded by using either member or nonmember functions. However, you can use only member functions to overload the following operators.
大多数运算符都可以通过成员或非成员函数进行重载,但下面的运算符只能通过成员函数进行重载。
Operator | Description |
---|---|
= | Assignment operator |
() | Function call operator |
[] | Subscripting operator |
-> | Class member access by pointer operator |
- 可重载的运算符
2.3. 重载运算符 -
和 *
- time.h
// Time class before operator overloading
#ifndef TIME_H_
#define TIME_H_
class Time {
private:
int hours;
int minutes;
public:
Time();
Time(const int h, const int m = 0);
~Time();
void AddMinutes(const int m);
void AddHours(const int h);
void Reset(const int h = 0, const int m = 0);
Time operator+(const Time & time) const;
Time operator-(const Time & time) const;
Time operator*(const double factor) const;
void Show() const;
};
#endif
- time.cpp
#include <iostream>
#include "time.h"
Time::Time() {
std::cout << "Time::Time()" << std::endl;
hours = minutes = 0;
}
Time::Time(const int h, const int m) {
std::cout << "Time::Time(const int h, const int m): h=" << h << ", m=" << m << std::endl;
hours = h;
minutes = m;
}
Time::~Time() {
std::cout << "Time::~Time(): hours=" << hours << ", minutes=" << minutes << std::endl;
}
void Time::AddMinutes(const int m) {
minutes += m;
hours += minutes / 60;
minutes %= 60;
}
void Time::AddHours(const int h) {
hours += h;
}
void Time::Reset(const int h, const int m) {
hours = h;
minutes = m;
}
Time Time::operator+(const Time & time) const {
std::cout << "Time::operator+(const Time & time)" << std::endl;
Time sum;
sum.minutes = minutes + time.minutes;
sum.hours = hours + time.hours + sum.minutes / 60;
sum.minutes %= 60;
return sum;
}
Time Time::operator-(const Time & time) const {
std::cout << "Time::operator-(const Time & time)" << std::endl;
Time diff;
const int tot1 = time.minutes + 60 * time.hours;
const int tot2 = minutes + 60 * hours;
diff.minutes = (tot2 - tot1) % 60;
diff.hours = (tot2 - tot1) / 60;
return diff;
}
Time Time::operator*(const double factor) const {
std::cout << "Time::operator*(const double factor)" << std::endl;
Time result;
const long total_minutes = hours * factor * 60 + minutes * factor;
result.hours = total_minutes / 60;
result.minutes = total_minutes % 60;
return result;
}
void Time::Show() const {
std::cout << hours << " hours, " << minutes << " minutes";
}
- sample.cpp
#include <iostream>
#include "time.h"
int main() {
{
Time weeding(4, 35);
Time waxing(2, 47);
Time total;
Time diff;
Time yongqiang;
std::cout << "\nweeding time = ";
weeding.Show();
std::cout << std::endl;
std::cout << "waxing time = ";
waxing.Show();
std::cout << std::endl << std::endl;
total = weeding + waxing; // use operator+()
std::cout << "total work time = ";
total.Show();
std::cout << std::endl << std::endl;
diff = weeding - waxing; // use operator-()
std::cout << "weeding time - waxing time = ";
diff.Show();
std::cout << std::endl << std::endl;
yongqiang = total * 1.5; // use operator+()
std::cout << "adjusted work time = ";
yongqiang.Show();
std::cout << std::endl << std::endl;
}
return 0;
}
Time::Time(const int h, const int m): h=4, m=35
Time::Time(const int h, const int m): h=2, m=47
Time::Time()
Time::Time()
Time::Time()
weeding time = 4 hours, 35 minutes
waxing time = 2 hours, 47 minutes
Time::operator+(const Time & time)
Time::Time()
Time::~Time(): hours=7, minutes=22
Time::~Time(): hours=7, minutes=22
total work time = 7 hours, 22 minutes
Time::operator-(const Time & time)
Time::Time()
Time::~Time(): hours=1, minutes=48
Time::~Time(): hours=1, minutes=48
weeding time - waxing time = 1 hours, 48 minutes
Time::operator*(const double factor)
Time::Time()
Time::~Time(): hours=11, minutes=3
Time::~Time(): hours=11, minutes=3
adjusted work time = 11 hours, 3 minutes
Time::~Time(): hours=11, minutes=3
Time::~Time(): hours=1, minutes=48
Time::~Time(): hours=7, minutes=22
Time::~Time(): hours=2, minutes=47
Time::~Time(): hours=4, minutes=35
请按任意键继续. . .
References
[1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/
[2] C++ Primer Plus, 6th Edition, https://www.informit.com/store/c-plus-plus-primer-plus-9780321776402
- Operator Overloading
- Time on Our Hands: Developing an Operator Overloading Example