成员函数和运算符重载

自定义类型的行为

运算符重载概述

运算符重载允许我们为自定义类型定义运算符的行为,使代码更直观易读。

可以重载的运算符

  • 算术运算符:+, -, *, /, %
  • 关系运算符:==, !=, <, >, <=, >=
  • 逻辑运算符:&&, ||, !
  • 赋值运算符:=, +=, -=, *=, /=
  • 下标运算符:[]
  • 函数调用运算符()
  • 自增自减:++, --

基本语法


// 运算符重载的基本格式
ReturnType operator符号(参数列表) {
    // 实现逻辑
}

// 示例:重载 + 运算符
class Complex {
public:
    int real, imag;

    Complex(int r = 0, int i = 0) : real(r), imag(i) {}

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

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

int main() {
    Complex c1(3, 4);
    Complex c2(1, 2);

    Complex c3 = c1 + c2;  // 相当于 c1.operator+(c2)

    c3.print();  // 4 + 6i

    return 0;
}
                

成员函数 vs 友元函数

类型 语法 参数个数 适用场景
成员函数 返回类型 operator*(参数) n-1个 第一个操作数是对象本身
友元函数 friend 返回类型 operator*(参数1, 参数2) n个 需要访问私有成员或对称操作

class Complex {
private:
    int real, imag;

public:
    Complex(int r = 0, int i = 0) : real(r), imag(i) {}

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

    // 友元函数形式(支持 Complex + int)
    friend Complex operator+(const Complex& c, int n);
};

// 友元函数实现
Complex operator+(const Complex& c, int n) {
    return Complex(c.real + n, c.imag);
}

int main() {
    Complex c1(3, 4);

    Complex c2 = c1 + Complex(1, 2);  // 成员函数
    Complex c3 = c1 + 5;              // 友元函数

    return 0;
}
                

常用运算符重载示例

1. 算术运算符


class Fraction {
public:
    int num, den;  // 分子、分母

    Fraction(int n = 0, int d = 1) : num(n), den(d) {}

    // 加法
    Fraction operator+(const Fraction& other) {
        return Fraction(num * other.den + other.num * den,
                       den * other.den);
    }

    // 减法
    Fraction operator-(const Fraction& other) {
        return Fraction(num * other.den - other.num * den,
                       den * other.den);
    }

    // 乘法
    Fraction operator*(const Fraction& other) {
        return Fraction(num * other.num, den * other.den);
    }
};

int main() {
    Fraction a(1, 2);  // 1/2
    Fraction b(1, 3);  // 1/3

    Fraction c = a + b;  // 1/2 + 1/3 = 5/6

    return 0;
}
                

2. 关系运算符


class Point {
public:
    int x, y;

    Point(int x = 0, int y = 0) : x(x), y(y) {}

    // 比较两个点是否相等
    bool operator==(const Point& other) {
        return x == other.x && y == other.y;
    }

    // 比较两个点是否不相等
    bool operator!=(const Point& other) {
        return !(*this == other);  // 复用 ==
    }

    // 用于排序
    bool operator<(const Point& other) {
        if (x != other.x) return x < other.x;
        return y < other.y;
    }
};

int main() {
    Point p1(1, 2);
    Point p2(1, 2);
    Point p3(2, 3);

    cout << (p1 == p2) << endl;  // 1 (true)
    cout << (p1 != p3) << endl;  // 1 (true)
    cout << (p1 < p3) << endl;   // 1 (true)

    return 0;
}
                

3. 下标运算符 []


class Array {
private:
    int data[100];
    int size;

public:
    Array() : size(0) {}

    // 重载下标运算符(可读可写)
    int& operator[](int index) {
        return data[index];  // 返回引用,可以修改
    }

    // const版本(只读)
    const int& operator[](int index) const {
        return data[index];
    }

    void push_back(int val) {
        data[size++] = val;
    }

    int getSize() { return size; }
};

int main() {
    Array arr;
    arr.push_back(10);
    arr.push_back(20);
    arr.push_back(30);

    cout << arr[0] << endl;  // 10

    arr[1] = 99;  // 可以修改
    cout << arr[1] << endl;  // 99

    return 0;
}
                

4. 赋值运算符 =


class String {
private:
    char* data;
    int len;

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

    // 拷贝构造函数
    String(const String& other) {
        len = other.len;
        data = new char[len + 1];
        strcpy(data, other.data);
    }

    // 赋值运算符重载
    String& operator=(const String& other) {
        if (this != &other) {  // 防止自赋值
            delete[] data;  // 释放原有内存
            len = other.len;
            data = new char[len + 1];
            strcpy(data, other.data);
        }
        return *this;
    }

    ~String() {
        delete[] data;
    }

    void print() {
        cout << data << endl;
    }
};

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

    s2 = s1;  // 调用赋值运算符
    s1.print();  // Hello
    s2.print();  // Hello

    return 0;
}
                

交互式演示:复数运算器

尝试重载的运算符计算复数:

复数1: + i
复数2: + i
点击按钮进行计算
等待输入...

注意事项

运算符重载的原则

  • 保持语义:重载后的行为应该符合直觉
  • 不能改变优先级:运算符优先级保持不变
  • 不能创建新运算符:只能重载已有的运算符
  • 至少有一个参数是类类型:不能重载基本类型的运算
  • 返回引用:赋值、下标等运算符通常返回引用

不能重载的运算符

  • :: 作用域解析运算符
  • . 成员访问运算符
  • .* 成员指针访问运算符
  • ?: 条件运算符
  • sizeof 大小运算符
  • typeid 类型识别运算符