【问题标题】:(C++) What's the difference between these overloaded operator functions?(C++) 这些重载的操作符函数有什么区别?
【发布时间】:2011-02-19 17:05:48
【问题描述】:

这两种重载下面的 != 运算符的方法有什么区别。哪个比较好?

Class Test  
{  
...//
private:  
int iTest

public:  
BOOL operator==(const &Test test) const;
BOOL operator!=(const &Test test) const;  
}

BOOL operator==(const &Test test) const  
{  
    return (iTest == test.iTest);  
}    

//overload function 1  
BOOL Test::operator!=(const &Test test) const  
{  
    return !operator==(test);  
}

//overload function 2  
BOOL Test::operator!=(const &Test test) const  
{  
    return (iTest != test.iTest);  
}  

我最近刚刚看到函数 1 调用同级运算符函数的语法,想知道这样编写是否有任何好处。

【问题讨论】:

  • 如果您将代码缩进 4 个空格(或将其包装在
     标签中),SO 将对其进行格式化,以便我们更容易阅读。
  • 在您的班级定义 (reddit.com/r/programming/comments/b8ws6) 后缺少 ;BOOL 是什么?
  • 对于 C++ 语言,您应该使用 bool,这是一个已定义的类型。标识符BOOL 是一种流行的标识符,用于模拟通常使用 C 语言的布尔类型。 BOOL 可能是 unsigned charunsigned int,具体取决于平台(甚至没有定义!)。
  • 我使用的是 MFC 库中的 BOOL。你说得对,我应该使用 bool。
  • 我更喜欢版本 3:free function operator== 和 free function operator!= 按照前者的方式实现,与版本 1 类似。free functions 的优点是您可以获得您无法获得的类型对称性使用成员方法(不会在左侧执行隐式转换)

标签: c++ class function operators overloading


【解决方案1】:

它们几乎肯定会编译成相同的机器码。

我更喜欢选择 2,只是因为我觉得说“operator==”很尴尬。但你可以使用

return ! (*this == test)

恕我直言,这也很清楚易懂。

【讨论】:

  • 好点。我忘了只是比较班级。谢谢,非常感谢。
【解决方案2】:

您的第一个重载确保在您的类型上调用 != 将始终提供与调用 == 相反的结果,即使您的 == 的实现会发生变化。

您的第二个重载函数没有,因为可以为== 提供任何实现并在将来更改现有实现。

如果您想确保!= 始终与== 相反,请使用第一个(以额外的函数调用为代价,无论如何这很可能会被内联)。

这是一个很好的例子。假设您有一个类 Point2D,其中包含字段 xy。如果要实现==,则必须在字段x 和字段y 上进行比较。如果您通过调用operator== 来实现!=,那么您的代码会更短,并且如果您曾经使用极坐标表示,那么需要更改的函数就会减少。

随着类字段的变化,等式测试和比较总是容易受到维护错误的影响。尽量减少直接访问状态的方法的数量可以降低出错的风险。

【讨论】:

  • 这很好。我从来没有这样想过。谢谢。
  • 还有一个事实是,如果你碰巧将函数 2 更改为 return (*this != test);(因为建议函数 1 使用 return !(*this == test);),你会遇到问题,因为重载的 operator!= 函数会调用自己。换句话说,使用函数 1 有一个额外的好处,那就是避免这种令人头疼的调试问题。
【解决方案3】:

版本 #1,恕我直言,虽然语法丑陋,但允许您在一个地方更改相等逻辑(在 == 运算符重载中)。它保证两个重载总是同步的。

【讨论】:

    【解决方案4】:

    我可以想到很多原因(或者可能是同一原因的某些方面)以这种方式编写它。他们都归结为:DRY

    • 确保两个对象始终是==!=

    • 如果您决定更改类中的内容或用于相等性测试的内容,您只需在一处进行更改

    我认为从概念上讲,您在这里定义了两个不同的东西:

    • Test 类“平等”的定义

    • 一个有用的接口,使用这个类的人可以确定他们的实例是否相等

    使用方法 2,您同时进行了临时操作。使用方法 1,您在 operator== 中定义相等,并通过 operator!= 提供接口的其余部分。

    一些语言/库更进一步,例如,在 Ruby 中,您可以定义 just <=> 来比较有序对象并混合 Comparable 并获得相等、不等式和 @987654329 @。

    【讨论】:

    【解决方案5】:

    一般来说,如下声明:
    return !(*this == object);
    允许您根据一个函数定义!=。在继承的世界里,子对象只需要定义operator==就可以使用基类operator!=

    struct Base
    {
      virtual bool isEqual(const Base& other) const = 0;
      bool  operator==(const Base& other) const
      {
        return isEqual(other);
      }
      bool  operator!=(const Base& other) const
      {
        return !(*this == other);  // Uses Base::operator==
      }
    };
    

    使用上述基类,使用!= 定义operator!= 将需要后代实现更多方法。

    另外,!(*this == other) 允许为!= 定义一个全局通用函数:

    template <typename T>
    bool operator!=(const T& a, const T& b)
    {
      return !(a == b);
    }
    

    虽然这种模式对==!= 的作用不大,但使用关系运算符时差异更大:&lt;, &lt;=, &gt;, &gt;=

    【讨论】:

      【解决方案6】:

      一般来说,相互关联地实现相关功能总是更好。对于操作员,总是有组。比如!=可以按照==来实现;后增量可以按照前增量来实现; &gt;&lt;=&gt;= 可以按照 &lt; 来实现(参见 &lt;utility&gt; 中的 std::rel_ops)等。如果类的某些内容需要更改,您只需要修改核心操作符,并且其余所有内容将自动更新以反映新行为。

      所有“辅助”运算符的一般实现总是相同的,因此甚至还有一个库可以自动提供根据少数提供的运算符定义的运算符。见Boost.Operators

      你的例子:

      #include <boost/operators.hpp>
      
      class Test : boost::equality_comparable<Test, Test>
      {
      private:
          int iTest;
      
      public:
          bool operator==(const Test& test) const;
          //look, no operator !=
      };
      
      int main()
      {
          Test a, b;
          a != b; //still works
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-01
        • 2012-06-15
        • 2015-05-21
        • 2015-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多