C++基础教程面向对象(学习笔记(25))

重载递增和递减运算符

重载递增(++)和递减(–)运算符非常简单,只有一些小小的不同。实际上有两个不同的递增和递减运算符:前缀增量和减量(例如++x; --y;)和后缀增量和减量(例如x++; y–;)。

因为递增和递减运算符都是一元运算符并且它们修改了它们的操作数,所以它们最好作为成员函数重载。我们将首先解决前缀版本,因为它们是最直接的。

重载前缀增量和减量

前缀增量和减量与任何普通的一元运算符完全相同。我们将通过示例执行此操作:
**

#include <iostream>
class Digit
{
private:
    int m_digit;
public:
    Digit(int digit=0)
        : m_digit(digit)
    {
    }
 
    Digit& operator++();
    Digit& operator--();
 
    friend std::ostream& operator<< (std::ostream &out, const Digit &d);
};
 
Digit& Digit::operator++()
{
    // 如果我们的数字已经是9,请置0
    if (m_digit == 9)
        m_digit = 0;
    //否则递增到下一个数字
    else
        ++m_digit;
 
    return *this;
}
 
Digit& Digit::operator--()
{
    //如果我们的数字已经是0,请置9
    if (m_digit == 0)
        m_digit = 9;
    //否则递减到下一个数字
    else
        --m_digit;
 
    return *this;
}
 
std::ostream& operator<< (std::ostream &out, const Digit &d)
{
	out << d.m_digit;
	return out;
}
 
int main()
{
    Digit digit(8);
 
    std::cout << digit;
    std::cout << ++digit;
    std::cout << ++digit;
    std::cout << --digit;
    std::cout << --digit;
 
    return 0;
}

**
我们的Digit类保持一个介于0和9之间的数字。我们已经重载了递增和递减,因此它们递增/递减数字,如果数字递增/递减范围则回绕。

此示例打印:
89098
注意,我们返回* this。重载的递增和递减运算符返回当前隐式对象,因此可以将多个运算符“链接”在一起。

重载后缀增量和减量

通常,当函数具有相同的名称但具有不同的数量和/或不同类型的参数时,它们可以被重载。但是,请考虑前缀和后缀增量和减量运算符的情况。两者都具有相同的名称(例如,运算符++),是一元的,并且采用相同类型的一个参数。那么在重载时如何区分这两者呢?

答案是C ++对后缀运算符使用“虚拟变量”或“伪参数”。此参数是伪整数参数,仅用于区分增量/减量的后缀operator与前缀operator。以下是带有前缀和后缀重载的上述Digit类:

class Digit
{
private:
    int m_digit;
public:
    Digit(int digit=0)
        : m_digit(digit)
    {
    }
 
    Digit& operator++(); // 前缀
    Digit& operator--(); // 前缀
 
    Digit operator++(int); //后缀
    Digit operator--(int); // 后缀
 
    friend std::ostream& operator<< (std::ostream &out, const Digit &d);
};
 
Digit& Digit::operator++()
{
    //  如果我们的数字已经是9,请置0
    if (m_digit == 9)
        m_digit = 0;
    // 否则递增到下一个数字
    else
        ++m_digit;
 
    return *this;
}
 
Digit& Digit::operator--()
{
    //如果我们的数字已经是0,请置9
    if (m_digit == 0)
        m_digit = 9;
    // 否则递减到下一个数字
    else
        --m_digit;
 
    return *this;
}
 
Digit Digit::operator++(int)
{
    // 创建一个临时变量用来临时保存当前的digit
    Digit temp(m_digit);
 
    // 调用前缀运算符递增此数字
    ++(*this); // 调用operator++
 
    // 返回临时结果
    return temp; //返回保存状态
}
 
Digit Digit::operator--(int)
{
    // 创建一个临时变量用来临时保存当前的digit
    Digit temp(m_digit);
 
    // 调用前缀运算符递减此数字
    --(*this); // 调用operator--
 
    //返回临时结果
    return temp; // 返回保存状态
}
 
std::ostream& operator<< (std::ostream &out, const Digit &d)
{
	out << d.m_digit;
	return out;
}
 
int main()
{
    Digit digit(5);
 
    std::cout << digit;
    std::cout << ++digit; // calls Digit::operator++();
    std::cout << digit++; // calls Digit::operator++(int);
    std::cout << digit;
    std::cout << --digit; // calls Digit::operator--();
    std::cout << digit--; // calls Digit::operator--(int);
    std::cout << digit;
 
    return 0;
}

这打印
5667665
这里有一些有趣的事情。首先,请注意我们通过在后缀版本上提供整数虚拟参数来区分前缀和后缀运算符。其次,因为在函数实现中没有使用伪参数,所以我们甚至没有给它起一个名字。这告诉编译器将此变量视为占位符,这意味着它不会警告我们我们声明了一个变量但从未使用它。

第三,请注意前缀和后缀运算符执行相同的工作 - 它们都会递增或递减对象。两者之间的差异在于它们返回的值。重载前缀运算符在递增或递减后返回对象。因此,重载这些是相当简单的。我们只是增加或减少我们的成员变量,然后返回* this。

另一方面,后缀运算符需要在对象递增或递减之前返回对象的状态。这会导致一些难题 - 如果我们递增或递减对象,我们将无法返回对象在递增或递减之前的状态。另一方面,如果我们在递增或递减之前返回对象的状态,则永远不会调用递增或递减。

解决此问题的典型方法是使用临时变量,该变量在增加或减少之前保存对象的值。然后,对象本身可以递增或递减。最后,临时变量返回给调用者。以这种方式,调用者在增加或减少之前接收对象的副本,但是对象本身是递增或递减的。请注意,这意味着重载运算符的返回值必须是非引用,因为我们不能返回对函数退出时将被销毁的局部变量的引用。另请注意,这意味着后缀运算符的效率通常低于前缀运算符,因为实例化临时变量并按值而不是引用返回会增加额外开销。

最后,请注意我们已经以这样的方式编写了后增量和后减量,它调用了前增量和前减量来完成大部分工作。这减少了重复的代码,使我们的类将来更容易修改。

猜你喜欢

转载自blog.csdn.net/qq_41879485/article/details/83108329