18、运算符重载

厨子大约 3 分钟C++C++基础编程程序厨

通过运算符重载,程序员可以为用户自定义类型(如类或结构体)定义运算符,使代码的行为更容易理解。

运算符重载的概念与语法

概念

运算符重载是指为自定义类型重新定义C++内置运算符的行为。例如,可以通过重载+运算符,使两个自定义类型的对象能够像基本数据类型一样进行加法运算。

语法

运算符重载通过定义特殊的成员函数或全局函数来实现。重载运算符的函数名由关键字operator和要重载的运算符组成。

成员函数形式

返回类型 operator运算符(参数列表);

全局函数形式

返回类型 operator运算符(参数1, 参数2);

常见运算符的重载实现

算术运算符重载(+-*/

示例:复数类的加法运算符重载

#include <iostream>
using namespace std;

class Complex {
private:
    double real;
    double imag;

public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

    // 成员函数形式重载+
    Complex operator+(const Complex& other) {
        return Complex(real + other.real, imag + other.imag);
    }

    // 成员函数形式重载-
    Complex operator-(const Complex& other) {
        return Complex(real - other.real, imag - other.imag);
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(3.0, 4.0);
    Complex c2(1.0, 2.0);

    Complex c3 = c1 + c2; // 使用重载的+运算符
    c3.display(); // 输出:4 + 6i

    Complex c4 = c1 - c2; // 使用重载的-运算符
    c4.display(); // 输出:2 + 2i

    return 0;
}

关系运算符重载(==!=<>

示例:复数类的相等运算符重载

#include <iostream>
using namespace std;

class Complex {
private:
    double real;
    double imag;

public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

    // 成员函数形式重载==
    bool operator==(const Complex& other) {
        return (real == other.real) && (imag == other.imag);
    }

    // 成员函数形式重载!=
    bool operator!=(const Complex& other) {
        return !(*this == other);
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

int main() {
    Complex c1(3.0, 4.0);
    Complex c2(3.0, 4.0);
    Complex c3(1.0, 2.0);

    if (c1 == c2) {
        cout << "c1 and c2 are equal." << endl;
    } else {
        cout << "c1 and c2 are not equal." << endl;
    }

    if (c1 != c3) {
        cout << "c1 and c3 are not equal." << endl;
    } else {
        cout << "c1 and c3 are equal." << endl;
    }

    return 0;
}

赋值运算符重载(=

示例:字符串类的赋值运算符重载

#include <iostream>
#include <cstring>
using namespace std;

class MyString {
private:
    char* str;

public:
    MyString(const char* s = "") {
        str = new char[strlen(s) + 1];
        strcpy(str, s);
    }

    // 析构函数
    ~MyString() {
        delete[] str;
    }

    // 赋值运算符重载
    MyString& operator=(const MyString& other) {
        if (this == &other) {
            return *this; // 处理自我赋值
        }
        delete[] str; // 释放原有内存
        str = new char[strlen(other.str) + 1];
        strcpy(str, other.str);
        return *this;
    }

    void display() {
        cout << str << endl;
    }
};

int main() {
    MyString s1("Hello");
    MyString s2("World");

    s1.display(); // 输出:Hello
    s2.display(); // 输出:World

    s2 = s1; // 使用重载的=运算符
    s2.display(); // 输出:Hello

    return 0;
}

流插入和流提取运算符重载(<<>>

示例:复数类的流提取运算符重载

#include <iostream>
using namespace std;

class Complex {
private:
    double real;
    double imag;

public:
    Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

    // 友元函数形式重载<<
    friend ostream& operator<<(ostream& os, const Complex& c);
};

ostream& operator<<(ostream& os, const Complex& c) {
    os << c.real << " + " << c.imag << "i";
    return os;
}

int main() {
    Complex c;
    cout << c << endl; // 使用重载的<<运算符
    return 0;
}

注意事项与限制

注意事项

  • 保持语义一致性:重载的运算符应与其原始语义一致。例如,+运算符应实现加法操作,而不是减法操作。
  • 处理自我赋值:在重载赋值运算符时,需要处理自我赋值的情况。

限制

  • 以下运算符不能重载:
    • 成员访问运算符(.
    • 成员指针访问运算符(.*
    • 作用域解析运算符(::
    • 条件运算符(?:
    • sizeof运算符
  • 不能改变运算符的优先级和结合性:重载运算符的优先级和结合性与原始运算符相同。
  • 至少有一个操作数是用户自定义类型:不能为基本数据类型重载运算符。

练习

  1. 设计一个Matrix类,重载+-*运算符,实现矩阵的加法、减法和乘法运算。
  2. 实现一个Fraction类,重载+-*/运算符,实现分数的加减乘除运算。
  3. 设计一个Date类,重载++--运算符,实现日期的递增和递减操作。