【问题标题】:Overloading both operator< and operator> in the same class在同一个类中重载 operator< 和 operator>
【发布时间】:2010-12-14 04:20:21
【问题描述】:

在我的作业中,我要设计一个类Message;在其他属性中,它具有“优先级”属性(主要目标是实现优先级队列)。

在容器中我必须检查一个对象是否大于另一个,我重载了运算符'>'。现在,我有一些关于它的一般性问题......

问题一:

如果我重载运算符'>',我是否应该为参数(const Message&、const Message&)重载运算符'

我的观点是同时重载 > 和

if(message1 > message2)
   { ... }

(下面的代码是为message1对象调用operator>,还是调用operator

但是,如果我像这样使用运算符会怎样:

if(message1 < message2)
   { ... }

operator> 被声明为友元函数:

friend bool operator>(const Message& m1, const Message& m2)

是否需要声明为成员函数?

谢谢。

【问题讨论】:

    标签: c++ operator-overloading


    【解决方案1】:

    如果我重载运算符'>',我是否应该为argumenst(const Message&,const Message&)重载运算符'

    是的。事实上,在大多数代码中,习惯使用&lt; 而不是&gt;(不要问我为什么,可能是历史原因)。但更一般地说,总是重载完整的相关运算符集;在您的情况下,这也可能是 ==!=&lt;=&gt;=

    (下面的代码是为message1对象调用operator>,还是为operator?)

    它总是调用它在代码中找到的东西。对于 C++ 编译器,&gt;&lt; 之间绝对没有联系。对我们来说,它们看起来很相似,但编译器会看到两个完全不同的、不相关的符号。所以没有歧义:编译器调用它所看到的。

    是否需要声明为成员函数?

    没有。事实上,最好不要声明为成员函数。将其声明为成员函数意味着第一个参数(即表达式的左侧)实际上必须是 Message 对象,而不是可以隐式转换为 Message 的对象。

    要理解这一点,请考虑以下情况:

    struct RealFraction {
        RealFraction(int x) { this.num = x; this.den = 1; }
        RealFraction(int num, int den) { normalize(num, den); }
        // Rest of code omitted.
    
        bool operator <(RealFraction const& rhs) {
            return num * rhs.den < den * rhs.num;
        }
    };
    

    现在可以写出如下比较:

    int x = 1;
    RealFraction y = 2;
    if (y < x) …
    

    但你不能写下以下内容:

    if (x < y) …
    

    虽然存在从intRealFraction 的隐式转换(使用第一个构造函数)。

    另一方面,如果您使用非成员函数来实现该运算符,则两种比较都将起作用,因为 C++ 会知道在第一个参数上调用隐式构造函数。

    【讨论】:

    • 谢谢,我在某处读到,如果我重载一个关系操作,重载所有这些操作似乎是合理的。
    • 感谢朋友/会员的回答。我明白你的意思:)。
    • 这个答案还应该指出,大多数关系运算符可以用少数几个运算符来描述,通常是&lt;==。例如,operator&gt;= 可以写成:bool operator&gt;=(const T&amp; l, const T&amp; r) { return !(l &lt; r); } 实际上,它们应该这样写。
    【解决方案2】:

    是的,您应该...但是您可以(并且可以说应该)实现三个&lt;&gt;&lt;=&gt;= 中的另一个。这确保了它们的行为一致。通常&lt; 是其他实现的运算符,因为它是sets 和maps 中使用的默认运算符。

    例如如果你实现了&lt;,你可以像这样定义&gt;&lt;=&gt;=

    inline bool operator>(const Message& lhs, const Message& rhs)
    {
        return rhs < lhs;
    }
    
    inline bool operator<=(const Message& lhs, const Message& rhs)
    {
        return !(rhs < lhs);
    }
    
    inline bool operator>=(const Message& lhs, const Message& rhs)
    {
        return !(lhs < rhs);
    }
    

    ==!= 通常是分开实现的。有时类实现== 使得a == b 当且仅当!(a &lt; b) &amp;&amp; !(b &lt; a) 但有时== 实现为比!(a &lt; b) &amp;&amp; !(b &lt; a) 更严格的关系。但是,这样做确实会导致课程客户更加复杂。

    在某些情况下,&lt;&gt;&lt;=&gt;= 是可以接受的,但 ==!= 是不可接受的。

    【讨论】:

    • 是的,是的,是的!我忘了提这个,但这很重要。您可能还应该说,您的干净代码将与手动和独立地对每个比较进行编码一样高效。这非常重要,因为这意味着任何为了提高性能而放弃此建议的尝试都是过早的优化。
    • 因此也可以使用捷径,例如class Message: boost::less_than_comparable&lt;Message, Message&gt;&lt;utility&gt; 中的rel_ops(后者闻起来有点可疑,因为它们可以通过 using 声明使用)。两个operator&lt; 就够了,其他的就用它来实现。
    • OTOH,尝试使用rel_ops 可能非常痛苦。通常,您必须将它们导入另一个命名空间,并且它们往往过于贪婪。如果您的类在全局命名空间中,您最终可以使用 rel_ops 为各种不合适的类型提供比较。
    • 我认为您宁愿在实现文件中使用rel_ops 来实现ad hoc 比较功能,而不是为类永久提供比较运算符。
    • @UncleBens:好吧,我误会了。不过,我仍然不确定我是否理解您的意思。你的意思是像{ using std::rel_ops::operator&gt;; return lhs &gt; rhs; } 这样的操作符的主体>?
    【解决方案3】:

    如果赋值没有明确要求使用运算符重载,您也可以考虑使用函数对象。原因是可能有不止一种方法可以比较两条消息的“小于”(例如按字典顺序比较内容、发布时间等),因此operator&lt; 的含义并不直观。

    std::priority_queue 使用的函数对象被指定为第三个模板参数(不幸的是,您还需要指定第二个底层容器类型):

    #include <queue>
    #include <string>
    #include <functional>
    #include <vector>
    
    class Message
    {
        int priority;
        std::string contents;
        //...
    public:
        Message(int priority, const std::string msg):
            priority(priority),
            contents(msg)
        {}
        int get_priority() const { return priority; }
        //...
    };
    
    struct ComparePriority:
        std::binary_function<Message, Message, bool> //this is just to be nice
    {
        bool operator()(const Message& a, const Message& b) const
        {
            return a.get_priority() < b.get_priority();
        }
    };
    
    int main()
    {
        typedef std::priority_queue<Message, std::vector<Message>, ComparePriority> MessageQueue;
        MessageQueue my_messages;
        my_messages.push(Message(10, "Come at once"));
    }
    

    在实现自己的优先级队列时,可以通过以下方式进行:

    class MessageQueue
    {
        std::vector<Message> messages;
        ComparePriority compare;
        //...
        void push(const Message& msg)
        {
            //...
            if (compare(msg, messages[x])) //msg has lower priority
            //...
        }
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-18
      • 1970-01-01
      • 2012-11-09
      • 1970-01-01
      相关资源
      最近更新 更多