如何重载运算符 +

How to overload the operator +?

本文关键字:运算符 重载 何重载      更新时间:2023-10-16

有人可以用初学者的术语解释一下运算符重载的工作原理吗? 我需要将"+"运算符重载为成员函数,并链接到对象数组。 我从谷歌上读了一些东西,它似乎与"+"的操作方式相同,而不会过载,所以我真的很困惑。

这就是我到目前为止的声明:

ALIST<t_type> & operator+(const t_type );

所以我在我的 ALIST 课程的公共部分声明了这一点。 "+"运算符现在是重载,还是在此函数定义中需要做更多工作才能使其重载?

另外,一旦它最终超载,我现在可以用它做什么?超载有什么意义?

请用初学者的术语解释,谢谢。

下面是一个原因的例子。

假设您正在实现一类特殊向量。例如Color类。 Color基本上是具有 4 个值的向量,r,g,b,a。

现在我们正在编写一个图形引擎,在我们的程序中,我们经常想知道当来自两个不同光源的光作用于我们正在评估的像素(如光线追踪器)时,像素会发生什么。定义一个 + 运算符来评估当来自两个不同光源的光相加时会发生什么情况会很方便。

如果我们没有运算符,您可以在代码中编写两个Color加在一起:

Color newColor = Color(
    color1.r + color2.r, 
    color1.g + color2.g,
    color1.b + color2.b,
    color1.a + color2.a
);

更糟糕的是,如果您正在密切研究光的物理特性,您可能会发现颜色无法正常添加。例如,它们可能会根据某些线性函数进行加法,例如 f(a) = a^2 ...(我不认为光不会这样做,它只是一个随机的例子)。

f(a) = a^2; f(b) = b^2
f(a + b) = ??
a = f(a)^.5; b = f(b)^.5
a + b = f(a)^.5 + f(b)^.5
f(a + b) = (f(a)^.5 + f(b)^.5)^2 *yada yada yada i'm terrible at math.

这意味着我们添加代码Color现在变成了

Color newColor = Color(
    pow(pow(color1.r, .5) + pow(color2.r, .5),2), 
    pow(pow(color1.g, .5) + pow(color2.g, .5),2),
    pow(pow(color1.b, .5) + pow(color2.b, .5),2),
    pow(pow(color1.a, .5) + pow(color2.a, .5),2), 
);

写出来很痛苦。但是,当然,如果我们采用Color类,并覆盖add运算符为我们完成所有这些操作,则可以在我们的代码中编写

Color color = color1 + color2;
在类定义中

定义Color

Color Color::operator+(const Color &rhs) const {
     return Color(
           pow(pow(this.r, .5) + pow(rhs.r, .5),2), 
           pow(pow(this.g, .5) + pow(rhs.g, .5),2),
           pow(pow(this.b, .5) + pow(rhs.b, .5),2),
           pow(pow(this.a, .5) + pow(rhs.a, .5),2)
     ); 
}

由于我们的特殊添加代码只在一个地方,你可以更好地优化它,并且程序其余部分的代码变得更加可读。

至少这是看待它的一种方式。过去,我更喜欢像addLights(color1, color2)这样的函数,因为它更容易编码,同样容易阅读,而且更具可读性,因为很明显它不是传统的vector添加。我敢打赌,你可以在整个职业生涯中不压倒运营商,我认为你不会错过太多。

如果您有一个自定义类,并且希望能够将该类的两个实例"添加"在一起,则 operator+ 重载将允许您自定义如何将两个对象"添加"在一起。

现在,您需要在 .h 或 .cpp 文件中实现 operator+ 方法。

到目前为止,你得到了一个正确的声明,然后你需要实现它,即通过将输入参数添加到内部管理的数组来填充正文,例如。

当你有这个到位时,你将能够说mylist = mylist + mytype;而不是mylist.add(mytype)例如。在这种情况下没什么大不了的,但是如果您正在开发应该自然地与标准运算符(如数学矩阵、向量、复数等)一起使用的类,或者如果您在ALIST类中覆盖[]运算符,那就更有意义了。

如前所述,必须有一个实现。 仅对重载进行原型设计并不能让编译器为您编写操作。

如果您是初学者(或者至少以"初学者术语"寻求答案),运算符重载不一定是直接在重要项目中使用的最佳主意。 涉及一些语义问题...因为+是一个非常抽象的符号,对于阅读代码的人来说可能意味着许多不同的事情,而更具描述性的名称可能会更好。

请注意,即使是标准库也不提供通过+=追加到向量之类的功能,因为它们存储了可能加在一起的元素......或附加向量...或在末尾添加一个元素...添加恶心[双关语]。 将"添加"的一个定义称为最"正确"是很棘手的:

为什么不重载 operator+=() for std::vector?

(请注意,由于std::string不存储任意数学类型,因此不那么模糊。

搜索调用

这些运算符方法的位置也可能很困难,因为要搜索的方法没有唯一的名称。 要找到调用,您必须筛选在算术和字符串类等上下文中使用的+

除了命名问题之外,我还会提到运算符重载会束缚您的手,如果您以后要传递其他参数。 如果您想在每次将某些内容放入列表时使用优先级参数进行扩展怎么办? 有一些技巧可以绕过它,例如将重载维护为默认实现以及使用参数化方法。 或者你可以做iostreams为<iomanip>采用的事情:

http://www.cplusplus.com/reference/iostream/manipulators/

但它只是指出了一个想法,即在潜入域中的运算符重载之前,应该谨慎使用。 另一个需要注意的微妙之处是使用类的全局"友元"函数重载与作为成员的重载之间的区别。 这里有一个关于这个问题的讨论...

运算符重载:成员函数与非成员函数?

重要的一点是,如果您想为非类(例如整数)出现在操作左侧的情况赋予意义,则无法向int添加方法。 所以你必须让它成为全局过载。

默认情况下,运算符符号设计为使用内置类型(int、char、string 等)。

C++ 的创建者无法定义操作数应如何为用户定义的类型工作,因此他实现了一个系统,您可以在其中重载它们并以在该对象的上下文中有意义的方式自行定义行为。

您通常会像这样重载运算符:

在类中。

class MyClass {
    int a;
    MyClass(int value) : a(value) {}
    MyClass MyClass::operator+(const MyClass&) const;
}

在课堂上.cpp

MyClass MyClass::operator+(const MyClass& other) const
{
    return MyClass(this->a+other.a);
}

这将返回一个新的MyClass,变量 A 设置为两个MyClass a 的值加在一起。完成此操作后,您可以执行以下操作:

MyClass first(2);
MyClass second(5);
MyClass last = first + second;
std::cout << last.a << std::endl; // prints 7, which is the result of 2+5

如果不在MyClass上重载operator+,将两个MyClass加在一起将是无效的。