美文网首页
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++多态。运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义。...

  • 1.2.15_C++ 关系运算符重载

    C++ 重载运算符和重载函数 C++ 语言支持各种关系运算符( < 、 > 、 <= 、 >= 、 == 等等),...

  • C++ 运算符重载

    运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义。实际上,很多C++运算符已经重载。将*运算符用...

  • C++运算符重载

    C++运算符重载的实质:运算符重载的实质就是函数重载或函数多态。运算符重载是一种形式的C++多态。目的在于让人能够...

  • C++运算符重载-下篇 (Boolan)

    C++运算符重载-下篇 (Boolan) 本章内容:1. 运算符重载的概述2. 重载算术运算符3. 重载按位运算符...

  • C++运算符重载-上篇 (Boolan)

    C++运算符重载-上篇 (Boolan) 本章内容:1. 运算符重载的概述2. 重载算术运算符3. 重载按位运算符...

  • C++ 重载运算符

    C++重载运算符

  • C++重载

    重载 C++语言规定: 重载的运算符要保持原运算符的意义。只能对已有的运算符重载,不能增加新的运算符。重载的运算符...

  • 1.2.17_C++ ++ 和 -- 运算符重载

    C++ 重载运算符和重载函数 递增运算符( ++ )和递减运算符( -- )是 C++ 语言中两个重要的一元运算符...

  • C++运算符重载详解

    运算符重载规则 1.被重载的运算符必须是已经存在的C++运算符,不能重载自己创建的运算符; 2.运算符被重载之后,...

网友评论

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

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