美文网首页
C++<第二十六篇>:重载运算符

C++<第二十六篇>:重载运算符

作者: NoBugException | 来源:发表于2022-01-25 20:49 被阅读0次

    重载运算符可以完成对象和对象之间的运算,同样也可以通过重载运算符运算实现对象和普通类型数据的运算。

    (1)重载运算符举例

    重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

    就以重载+运算符为例:

    class Rectangle
    {
    public:
    
        void setWidth(double width)
        {
            mWidth = width;
        }
    
        void setHeight(double height)
        {
            mHeight = height;
        }
    
        double getArea() 
        {
            return mWidth * mHeight;
        }
    
        // 重载 + 运算符,用于把两个 Rectangle 对象相加
        Rectangle operator+(const Rectangle& rect)
        {
            Rectangle rectangle;
            rectangle.setWidth(mWidth + rect.mWidth);
            rectangle.setHeight(mHeight + rect.mHeight);
            return rectangle;
        }
    
    private:
        double mWidth;     // 宽度
        double mHeight;      // 长度
    };
    

    创建一个 Rectangle 类,Rectangle 类有4个函数

    setWidth:设置矩形的宽度
    setHeight:设置矩形的长度
    getArea:设置矩形的面积
    operator+:实现+运算符重载,两个Rectangle对象相加

    调用方式如下:

    int main()
    {
        Rectangle* rect1 = new Rectangle();
        rect1->setWidth(10);
        rect1->setHeight(10);
        cout << "矩形的面积:" << rect1->getArea() << endl;
    
        Rectangle* rect2 = new Rectangle();
        rect2->setWidth(20);
        rect2->setHeight(20);
        cout << "矩形的面积:" << rect2->getArea() << endl;
    
        Rectangle rect3 = *rect1 + *rect2;
        cout << "矩形的面积:" << rect3.getArea() << endl;
    
        return 0;
    }
    

    重载运算符一般用于两个对象运算,还可以是对象和基本数据类型的计算,演示代码如下:

    #include <iostream>
    using namespace std;
    
    class Rectangle
    {
    public:
    
        void setWidth(double width)
        {
            mWidth = width;
        }
    
        void setHeight(double height)
        {
            mHeight = height;
        }
    
        double getArea() 
        {
            return mWidth * mHeight;
        }
    
        // 重载 + 运算符,用于把两个 Rectangle 对象相加
        Rectangle operator+(const int change)
        {
            Rectangle rectangle;
            rectangle.setWidth(mWidth + change);
            rectangle.setHeight(mHeight + change);
            return rectangle;
        }
    
    private:
        double mWidth;     // 宽度
        double mHeight;      // 长度
    };
    
    int main()
    {
        Rectangle* rect1 = new Rectangle();
        rect1->setWidth(10);
        rect1->setHeight(10);
        cout << "矩形的面积:" << rect1->getArea() << endl;
    
        Rectangle rect2 = *rect1 + 10;
        cout << "矩形的面积:" << rect2.getArea() << endl;
    
        return 0;
    }
    

    (2)可重载运算符和不可重载运算符

    下面是可重载的运算符列表:

    运算符类型 运算符
    双目算术运算符 + (加),-(减),*(乘),/(除),% (取模)
    关系运算符 ==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于)
    逻辑运算符 ||(逻辑或),&&(逻辑与),!(逻辑非)
    单目运算符 + (正),-(负),*(指针),&(取地址)
    自增自减运算符 ++(自增),--(自减)
    位运算符 (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
    赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
    空间申请与释放 new, delete, new[ ] , delete[]
    其他运算符 ()(函数调用),->(成员访问),,(逗号),

    不可重载运算符有:

    运算符 运算符类型
    .: 成员访问运算符
    ., -> 成员指针访问运算符
    ::: 域运算符
    sizeof: 长度运算符
    ?:: 条件运算符
    #: 预处理符号

    (3)一元运算符的重载

    常见的一元运算符有:自增加(++)、自减少(--)、逻辑非(!)

    自增加(++)和自减少(--)差不多,只举一个例子,就以自增加(++)举例,自增加(++)分为前置和后置,operator++() 表示前置, operator++(int) 表示后置,演示代码如下:

    class Card 
    {
    public:
    
        void setCardCount(int count) // 获取卡片数量
        {
            mCount = count;
        }
    
        int getCardCount() // 获取卡片数量
        {
            return mCount;
        }
    
        void operator++() // ++card,前置形式
        {
            ++mCount;
        }
    
        void operator++(int) // ++card,后置形式
        {
            mCount++;
        }
    
    private:
        int mCount; // 卡片的数量
    };
    
    int main()
    {
        Card card;
        card.setCardCount(10);
        cout << "已有卡片数量:" << card.getCardCount() << endl;
        ++card;
        cout << "前置自增1后的卡片数量:" << card.getCardCount() << endl;
        card++;
        cout << "后置自增1后的卡片数量:" << card.getCardCount() << endl;
        return 0;
    }
    

    逻辑非的实例代码如下:

    class Card 
    {
    public:
    
        void setIsCard(bool isCard)
        {
            this->isCard = isCard;
        }
    
        bool operator!()
        {
            return !isCard;
        }
    
    private:
        bool isCard; // 是否是卡片
    };
    
    int main()
    {
        Card* card = new Card();
        card->setIsCard(false);
        if (!(*card)) {
            cout << "this is a card" << endl;
        }
        else
        {
            cout << "this is not a card" << endl;
        }
        return 0;
    }
    

    (4)二元运算符的重载

    常见的二元运算符就是 加(+)、减(-)、乘(*)、除(/)

    文章一开始就有一个+运算符呀的例子,其它三个运算符和这个例子是一样的,所以就不重复举例了。

    (5)关系运算符的重载

    常见的关系运算符有:大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)。

    就以等于为例:

    class Line 
    {
    public:
    
        void setLength(int length)
        {
            this->length = length;
        }
    
        bool operator==(const Line line)
        {
            return length == line.length;
        }
    
    private:
        int length; // 线条的长度
    };
    
    int main()
    {
        Line* line1 = new Line();
        line1->setLength(10);
    
        Line* line2 = new Line();
        line2->setLength(10);
    
        if (*line1 == *line2) 
        {
            cout << "line1 == line2" << endl;
        }
        else
        {
            cout << "line1 != line2" << endl;
        }
        return 0;
    }
    

    (6)<< 和 >> 运算符

    在 C++ 中,标准库本身已经对左移运算符 << 和右移运算符 >> 分别进行了重载,使其能够用于不同数据的输入输出,但是输入输出的对象只能是 C++ 内置的数据类型(例如 bool、int、double 等)和标准库所包含的类类型(例如 string、complex、ofstream、ifstream 等)。

    如果我们自己定义了一种新的数据类型,需要用输入输出运算符去处理,那么就必须对它们进行重载,代码如下:

    class Name
    {
    public:
        void setName(string name) 
        {
            this->name = name;
        }
    
        string getName() 
        {
            return name;
        }
    
        friend istream& operator>>(istream& intput, Name& n)
        {
            intput >> n.name;
            return intput;
        }
    
        friend ostream& operator<<(ostream& output, const Name& n) 
        {
            output << n.name;
            return output;
        }
    
    private:
        string name;
    };
    
    int main()
    {
        cout << "please input name: "<< endl;
        Name* name = new Name();
        cin >> *name;
        cout << "my name is " << *name << endl;
    
        return 0;
    }
    

    定义一个Name类,使用

    Name* name = new Name();
    

    对Name建立实例,直接输入或者输出对象。

    在Name类中,对 << 和 >> 进行重载可以实现直接输出对象。

    在Name类中重载运算符 >>:

    friend istream& operator>>(istream& intput, Name& n)
    {
        intput >> n.name;
        return intput;
    }
    

    由于operator>>的形式参数个数大于一个,所以operator>>必须以全局的函数存在(非成员函数),即需要在函数前面添加关键字 friend
    另外,这个运算符主要是输入使用的,Name的name变量是要被赋值的,所以形式参数 n 不能是常量。

    在Name类中重载运算符 <<:

    friend ostream& operator<<(ostream& output, const Name& n) 
    {
        output << n.name;
        return output;
    }
    

    当前形式参数的 n 设置为常量,因为此函数的目的是将 n 输出,所以 n 不会被赋值(n 不会成为左值)。
    和 operator>> 函数一样,operator<<函数的形式参数也是2个,所以必须将 operator<< 函数作为全局函数。

    (7)赋值运算符的重载

    演示代码如下:

    class DengOp 
    {
    public:
        void setParam(int param) 
        {
            this->param = param;
        }
    
        int getParam() 
        {
            return param;
        }
    
        void operator= (const DengOp& op)
        {
            param = op.param * 2;
        }
    private:
        int param;                  
    };
    
    int main()
    {
        DengOp dOp, dOpBak;
        dOp.setParam(10);
    
        dOpBak = dOp;
    
        cout << dOpBak.getParam() << endl;
    
        return 0;
    }
    

    以上代码是同类型的对象相互赋值,不过已经将 param 值扩大的2倍。

    其中,operator= 不能做为全局函数,也就是说,此函数前面不支持添加 friend 关键字,且 operator= 的形式参数的数量只能是1。

    operator= 也可以做成不同类型对象相互赋值,代码如下:

    class DengOp2
    {
    public:
        void setParam(int param)
        {
            this->param = param;
        }
    
        int getParam()
        {
            return param;
        }
    
    private:
        int param;
    };
    
    class DengOp1 
    {
    public:
        void setParam(int param) 
        {
            this->param = param;
        }
    
        int getParam() 
        {
            return param;
        }
    
        void operator= (DengOp2& op2)
        {
            param = op2.getParam();
        }
    private:
        int param;                  
    };
    

    以上两个类分别是:DengOp1 和 DengOp2

    DengOp1 有 operator= 函数,DengOp2 中没有,调用代码如下:

    int main()
    {
        DengOp1 dOp1;
        dOp1.setParam(10);
        DengOp2 dOp2;
        dOp2.setParam(20);
        dOp1 = dOp2;
    
        cout << dOp1.getParam() << endl;
    
        return 0;
    }
    

    打印结果是:20。

    注意,不可以将 dOp1 赋值给 dOp2,如:

        dOp2 = dOp1;
    

    因为 DengOp2 中没有对赋值运算符重载。

    (8)类型强制转换运算符的重载

    类型强制转换运算符 又称 函数调用运算符,类型强制转换运算符就是 (),它的重载有两种玩法。

    【第一种玩法】

    class D 
    {
    public:
        void setA(int a) 
        {
            mA = a;
        }
        int getA() 
        {
            return mA;
        }
        D operator()(int a)
        {
            D d;
            d.setA(a);
            return d;
        }
    private:
        int mA;
    };
    
    int main()
    {
    
        D d1, d2;
        d1.setA(10);
        d2 = d1(20);
        cout << d2.getA() << endl;
    
        return 0;
    }
    

    【第二种玩法】

    class D 
    {
    public:
        void setA(int a) 
        {
            mA = a;
        }
        int getA() 
        {
            return mA;
        }
        operator double()
        {
            return mA;
        }
    private:
        int mA;
    };
    
    int main()
    {
    
        D d;
        d.setA(10);
        double n = (double)d;
        cout << n << endl;
    
        return 0;
    }
    

    (9)下标运算符 [] 重载

    下标操作符 [] 通常用于访问数组元素。重载该运算符用于增强操作 C++ 数组的功能。

    演示代码如下:

    class D 
    {
    
    public:
    
        void setArr() 
        {
            for (size_t i = 0; i < 10; i++)
            {
                arr[i] = i;
            }
        }
    
        int& operator[](int i)
        {
            if (i >= 10) // 如果索引大于10,则返回第一个元素
            {
                return arr[0];
            }
            return arr[i];
        }
    
    private:
        int arr[10];
    };
    
    int main()
    {
    
        D d;
        d.setArr();
    
        cout << d[2] << endl;
        cout << d[5] << endl;
        cout << d[12] << endl;
    
        return 0;
    }
    

    (10)总结

    还有一些运算符也可以实现重载,这里就不一一举例了。

    在 C++ 中进行运算符重载时,有以下问题需要注意:

    • 重载后运算符的含义应该符合原有用法习惯。例如重载+运算符,完成的功能就应该类似于做加法,在重载的+运算符中做减法是不合适的。此外,重载应尽量保留运算符原有的特性。
    • C++ 规定,运算符重载不改变运算符的优先级。
    • 以下运算符不能被重载:..*::? :sizeof
    • 重载运算符()[]->、或者赋值运算符=时,只能将它们重载为成员函数,不能重载为全局函数。
    运算符重载的实质是将运算符重载为一个函数,使用运算符的表达式就被解释为对重载函数的调用。
    
    运算符可以重载为全局(friend)函数。此时函数的参数个数就是运算符的操作数个数,运算符的操作数就成为函数的实参。
    
    运算符也可以重载为成员函数。此时函数的参数个数就是运算符的操作数个数减一,运算符的操作数有一个成为函数作用的对象,其余的成为函数的实参。
    
    必要时需要重载赋值运算符=,以避免两个对象内部的指针指向同一片存储空间。
    
    运算符可以重载为全局函数,然后声明为类的友元。
    
    << 和 >> 是在 iostream 中被重载,才成为所谓的“流插入运算符”和“流提取运算符”的。
    
    类型的名字可以作为强制类型转换运算符,也可以被重载为类的成员函数。它能使得对象被自动转换为某种类型。
    
    自增、自减运算符各有两种重载方式,用于区别前置用法和后置用法。
    
    运算符重载不改变运算符的优先级。重载运算符时,应该尽量保留运算符原本的特性。
    

    [本章完...]

    相关文章

      网友评论

          本文标题:C++<第二十六篇>:重载运算符

          本文链接:https://www.haomeiwen.com/subject/fbxyhrtx.html