C++ 运算符重载

发布于:2025-06-20 ⋅ 阅读:(14) ⋅ 点赞:(0)

运算符重载(Operator Overloading)允许为自定义类型(如类、结构体)赋予类似内置类型的运算符行为,使对象之间可以使用+、-、==、<<等运算符进行操作,提升代码的可读性和易用性。

1. 基本语法

• 通过operator关键字重载运算符。
• 可以作为成员函数全局函数实现。

示例(成员函数):

class Point {
public:
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
    Point operator+(const Point& other) const {
        return Point(x + other.x, y + other.y);
    }
};

2. 加号运算符重载

成员函数方式:适用于a + b,其中a为当前对象,b为参数

class Point {
public:
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
    // 加号运算符重载
    Point operator+(const Point& other) const {
        return Point(x + other.x, y + other.y);
    }
};

int main() {
    Point p1(1, 2), p2(3, 4);
    Point p3 = p1 + p2; // 调用operator+
    std::cout << p3.x << ", " << p3.y << std::endl; // 输出4, 6
    return 0;
}

全局函数方式:适用于需要支持a + b和b + a等更灵活的场景(如左操作数不是类对象)。

class Point {
public:
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
};

Point operator+(const Point& a, const Point& b) {
    return Point(a.x + b.x, a.y + b.y);
}

注意事项
• 推荐参数为const引用,返回新对象。
• 不建议修改参与运算的原对象(保持加法的“无副作用”)。
• 可重载为成员函数或全局函数,具体选择视需求而定。

总结:
加号运算符重载让自定义类型支持直观的“加法”操作,提升代码可读性和易用性。

3. 左移运算符重载

左移运算符重载(operator<<)常用于自定义类型的输出,使对象可以直接用std::cout << obj的方式打印内容。通常写成全局函数,并返回ostream&支持链式输出

#include <iostream>

class Point {
public:
    int x, y;
    Point(int x, int y) : x(x), y(y) {}
};

// 左移运算符重载,必须为全局函数(或友元)
std::ostream& operator<<(std::ostream& os, const Point& p) {
    os << "(" << p.x << ", " << p.y << ")";
    return os;
}

int main() {
    Point p(3, 4);
    std::cout << p << std::endl; // 输出:(3, 4)
    return 0;
}

说明与注意事项
• 第一个参数为std::ostream&,第二个参数为自定义类型的const引用。
• 返回ostream&,支持链式输出(如std::cout << p1 << p2;)。
• 通常声明为友元函数以访问私有成员:

friend std::ostream& operator<<(std::ostream&, const Point&);

4. 递增运算符

递增运算符重载(operator++)允许自定义类型支持前置后置自增(如++obj和obj++)。这两种写法的重载方式略有不同。

前置递增运算符重载:
• 语法:Type& operator++();
• 用于++obj,返回自增后的对象引用。

示例:

class Counter {
    int value;
public:
    Counter(int v = 0) : value(v) {}
    // 前置++
    Counter& operator++() {
        ++value;
        return *this;
    }
    int get() const { return value; }
};

后置递增运算符重载:
• 语法:Type operator++(int);(int是占位参数,用于区分前置和后置)
• 用于obj++,返回自增前的对象副本。

示例:

class Counter {
    int value;
public:
    Counter(int v = 0) : value(v) {}
    // 后置++
    Counter operator++(int) {
        Counter temp = *this;
        ++value;
        return temp;
    }
    int get() const { return value; }
};

调用:

int main() {
    Counter c(5);
    ++c;            // 前置,c变为6
    c++;            // 后置,c变为7
    std::cout << c.get() << std::endl; // 输出7
    return 0;
}

注意事项
• 前置返回引用,后置返回副本
推荐前置效率更高,后置因需保存临时副本略慢。
• 可根据需要只重载一种或两种。

5. 赋值运算符

赋值运算符重载(operator=)允许自定义类型支持对象间的赋值操作(如a = b;)。正确实现赋值运算符对于资源管理(如动态内存)尤为重要。

基本语法:

class Demo {
public:
    Demo& operator=(const Demo& other) {
        if (this != &other) { // 防止自赋值
            // 释放旧资源(如有)
            // 复制other的数据到当前对象
        }
        return *this; // 支持链式赋值
    }
};

典型实现:

class MyString {
    char* data;
public:
    MyString(const char* str = "") {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }
    ~MyString() { delete[] data; }
    MyString& operator=(const MyString& other) {
        if (this != &other) {
            delete[] data; // 释放旧内存
            data = new char[strlen(other.data) + 1];
            strcpy(data, other.data);
        }
        return *this;
    }
};

注意事项:
防止自赋值:if (this != &other),避免自我释放导致数据丢失。
释放旧资源:先释放当前对象持有的资源,防止内存泄漏。
深拷贝:如有指针成员,需分配新内存并复制内容。
返回自身引用:return *this;,支持链式赋值(如a = b = c;)。

6. 关系运算符重载

关系运算符重载允许自定义类型支持比较操作(如==、!=、<、>、<=、>=),使对象之间可以像内置类型一样进行比较。

常见关系运算符重载:
• operator==:等于
• operator!=:不等于
• operator< :小于
• operator> :大于
• operator<=:小于等于
• operator>=:大于等于

示例

class Point {
public:
    int x, y;
    Point(int x, int y) : x(x), y(y) {}

    // 等于运算符重载
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }

    // 小于运算符重载(按字典序)
    bool operator<(const Point& other) const {
        return (x < other.x) || (x == other.x && y < other.y);
    }
};

int main() {
    Point p1(1, 2), p2(1, 2), p3(2, 3);
    std::cout << std::boolalpha;
    std::cout << (p1 == p2) << std::endl; // true
    std::cout << (p1 < p3) << std::endl;  // true
    return 0;
}

注意事项:
• 推荐参数为const引用,成员函数加const保证不修改对象。
• 只需重载==和<,其余可用标准库std::rel_ops自动推导(或手动实现)。
• 关系运算符重载应符合直观语义,避免误用。

7. 函数调用运算符重载

函数调用运算符重载(operator())允许对象像函数一样被调用,这种对象称为“仿函数”或“函数对象”。

函数调用运算符重载(operator())
• 通过重载operator(),可以让对象像函数一样使用,常用于自定义算法、回调、STL等场景。

示例:

class Adder {
    int base;
public:
    Adder(int b) : base(b) {}
    int operator()(int x) const {
        return base + x;
    }
};

int main() {
    Adder add5(5);
    std::cout << add5(10) << std::endl; // 输出15
    return 0;
}

• 可以有多个参数,也可以重载多次实现不同参数列表。
• 支持捕获状态(如成员变量),比普通函数更灵活。

8. 其他运算符重载

下标运算符重载(operator[]):使对象支持数组下标访问
示例:

  class Array {
      int data[10];
  public:
      int& operator[](int idx) { return data[idx]; }
  };  

箭头运算符重载(operator->):使对象像指针一样访问成员。

  class Ptr {
      Demo* p;
  public:
      Demo* operator->() { return p; }
  };  

类型转换运算符重载(operator 类型):实现对象到其他类型的隐式或显式转换。

  class Demo {
      int value;
  public:
      operator int() const { return value; }
  };  

9. 注意事项

• 运算符重载应符合直观语义,避免滥用。
• 某些运算符(如[]、()、->、=)必须为成员函数。
• 运算符重载不能改变运算符的优先级和结合性。


网站公告

今日签到

点亮在社区的每一天
去签到