目录

第七章 运算符重载

本章将详细介绍C++的运算符重载机制,包括成员函数重载、友元函数重载、类型转换运算符等。

7.1 运算符重载基础

7.1.1 基本概念

运算符重载允许为自定义类型定义运算符的行为,使代码更直观、更自然。

#include <iostream>
using namespace std;
 
class Complex {
private:
    double real;
    double imag;
 
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
 
    // 成员函数重载 +
    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }
 
    // 成员函数重载 -
    Complex operator-(const Complex& other) const {
        return Complex(real - other.real, imag - other.imag);
    }
 
    // 成员函数重载 *(复数乘法)
    Complex operator*(const Complex& other) const {
        return Complex(
            real * other.real - imag * other.imag,
            real * other.imag + imag * other.real
        );
    }
 
    // 重载 << 输出运算符(友元函数)
    friend ostream& operator<<(ostream& os, const Complex& c) {
        os << c.real;
        if (c.imag >= 0) os << "+";
        os << c.imag << "i";
        return os;
    }
 
    // 重载 >> 输入运算符(友元函数)
    friend istream& operator>>(istream& is, Complex& c) {
        is >> c.real >> c.imag;
        return is;
    }
};
 
int main() {
    Complex c1(3, 4);
    Complex c2(1, 2);
 
    Complex c3 = c1 + c2;  // 等价于 c1.operator+(c2)
    Complex c4 = c1 - c2;
    Complex c5 = c1 * c2;
 
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    cout << "c1 + c2 = " << c3 << endl;  // 4+6i
    cout << "c1 - c2 = " << c4 << endl;  // 2+2i
    cout << "c1 * c2 = " << c5 << endl;  // -5+10i
 
    return 0;
}

7.1.2 可重载的运算符

运算符类别 可重载运算符
———–————-
算术 +, -, *, /, %
关系 ==, !=, <, >, ⇐, >=
逻辑 !, &&,
位运算 ~, &, , , «, »
赋值 =, +=, -=, *=, /=, %=, &=, =, =, «=, »=
自增/自减 ++, –
内存 new, delete, new[], delete[]
访问 [], (), →, →*, .*
其他 , (逗号), & (取地址), * (解引用)

不可重载的运算符:

7.2 成员函数重载

7.2.1 单目运算符重载

#include <iostream>
using namespace std;
 
class Counter {
private:
    int count;
 
public:
    Counter(int c = 0) : count(c) {}
 
    // 前缀 ++
    Counter& operator++() {
        ++count;
        return *this;
    }
 
    // 后缀 ++ (int是区分标记)
    Counter operator++(int) {
        Counter temp(*this);
        count++;
        return temp;
    }
 
    // 前缀 --
    Counter& operator--() {
        --count;
        return *this;
    }
 
    // 后缀 --
    Counter operator--(int) {
        Counter temp(*this);
        count--;
        return temp;
    }
 
    // 一元负号
    Counter operator-() const {
        return Counter(-count);
    }
 
    // 逻辑非
    bool operator!() const {
        return count == 0;
    }
 
    // 友元输出
    friend ostream& operator<<(ostream& os, const Counter& c) {
        os << c.count;
        return os;
    }
};
 
int main() {
    Counter c1(5);
 
    cout << "c1 = " << c1 << endl;
    cout << "++c1 = " << ++c1 << endl;  // 前缀,先增加后返回
    cout << "c1++ = " << c1++ << endl;  // 后缀,返回原值后增加
    cout << "c1 = " << c1 << endl;
 
    Counter c2 = -c1;
    cout << "-c1 = " << c2 << endl;
 
    cout << "!c1 = " << !c1 << endl;  // false (0)
 
    return 0;
}

7.2.2 赋值运算符重载

#include <iostream>
#include <cstring>
using namespace std;
 
class String {
private:
    char* data;
    size_t length;
 
public:
    // 构造函数
    String(const char* str = "") {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
    }
 
    // 拷贝构造函数
    String(const String& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
    }
 
    // 拷贝赋值运算符
    String& operator=(const String& other) {
        // 1. 检查自赋值
        if (this != &other) {
            // 2. 释放原有资源
            delete[] data;
 
            // 3. 复制新资源
            length = other.length;
            data = new char[length + 1];
            strcpy(data, other.data);
        }
        // 4. 返回自身引用
        return *this;
    }
 
    // 移动构造函数(C++11)
    String(String&& other) noexcept {
        data = other.data;
        length = other.length;
        other.data = nullptr;
        other.length = 0;
    }
 
    // 移动赋值运算符(C++11)
    String& operator=(String&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            length = other.length;
            other.data = nullptr;
            other.length = 0;
        }
        return *this;
    }
 
    // 析构函数
    ~String() {
        delete[] data;
    }
 
    // 友元输出
    friend ostream& operator<<(ostream& os, const String& s) {
        os << s.data;
        return os;
    }
};
 
int main() {
    String s1("Hello");
    String s2("World");
 
    s2 = s1;  // 拷贝赋值
    cout << "s2 = " << s2 << endl;
 
    String s3 = s1;  // 拷贝构造
 
    // 自赋值
    s1 = s1;  // 安全,因为有自赋值检查
 
    return 0;
}

7.2.3 下标运算符重载

#include <iostream>
#include <stdexcept>
using namespace std;
 
class Array {
private:
    int* data;
    size_t size;
 
public:
    Array(size_t n) : size(n) {
        data = new int[n]();
    }
 
    // 拷贝构造函数
    Array(const Array& other) : size(other.size) {
        data = new int[size];
        for (size_t i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
    }
 
    // 拷贝赋值
    Array& operator=(const Array& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = new int[size];
            for (size_t i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
        }
        return *this;
    }
 
    ~Array() {
        delete[] data;
    }
 
    // 非const版本
    int& operator[](size_t index) {
        if (index >= size) {
            throw out_of_range("Index out of bounds");
        }
        return data[index];
    }
 
    // const版本
    const int& operator[](size_t index) const {
        if (index >= size) {
            throw out_of_range("Index out of bounds");
        }
        return data[index];
    }
 
    size_t getSize() const { return size; }
};
 
int main() {
    Array arr(5);
 
    // 使用下标运算符
    for (size_t i = 0; i < arr.getSize(); i++) {
        arr[i] = static_cast<int>(i * 10);  // 调用非const版本
    }
 
    const Array& carr = arr;
    for (size_t i = 0; i < carr.getSize(); i++) {
        cout << carr[i] << " ";  // 调用const版本
    }
    cout << endl;
 
    // 越界访问会抛出异常
    try {
        arr[10] = 100;
    } catch (const exception& e) {
        cout << "Error: " << e.what() << endl;
    }
 
    return 0;
}

7.3 友元函数重载

7.3.1 二元运算符的友元重载

#include <iostream>
using namespace std;
 
class Complex {
private:
    double real, imag;
 
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
 
    // 友元函数可以访问私有成员
    // 左操作数是double,右操作数是Complex
    friend Complex operator+(double lhs, const Complex& rhs) {
        return Complex(lhs + rhs.real, rhs.imag);
    }
 
    // 对称性:也定义 Complex + double
    friend Complex operator+(const Complex& lhs, double rhs) {
        return Complex(lhs.real + rhs, lhs.imag);
    }
 
    // 两个Complex相加
    friend Complex operator+(const Complex& lhs, const Complex& rhs) {
        return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
    }
 
    // 相等比较
    friend bool operator==(const Complex& lhs, const Complex& rhs) {
        return lhs.real == rhs.real && lhs.imag == rhs.imag;
    }
 
    friend bool operator!=(const Complex& lhs, const Complex& rhs) {
        return !(lhs == rhs);
    }
 
    friend ostream& operator<<(ostream& os, const Complex& c) {
        os << c.real;
        if (c.imag >= 0) os << "+";
        os << c.imag << "i";
        return os;
    }
};
 
int main() {
    Complex c1(3, 4);
    Complex c2(1, 2);
 
    Complex c3 = c1 + c2;      // Complex + Complex
    Complex c4 = c1 + 5.0;     // Complex + double
    Complex c5 = 5.0 + c1;     // double + Complex (需要友元)
 
    cout << "c1 + c2 = " << c3 << endl;
    cout << "c1 + 5 = " << c4 << endl;
    cout << "5 + c1 = " << c5 << endl;
 
    cout << "c1 == c2: " << (c1 == c2) << endl;
    cout << "c1 != c2: " << (c1 != c2) << endl;
 
    return 0;
}

7.3.2 输入输出运算符重载

#include <iostream>
#include <string>
using namespace std;
 
class Date {
private:
    int year, month, day;
 
public:
    Date(int y = 2000, int m = 1, int d = 1) 
        : year(y), month(m), day(d) {}
 
    // 输出运算符
    friend ostream& operator<<(ostream& os, const Date& date) {
        os << date.year << "-" 
           << (date.month < 10 ? "0" : "") << date.month << "-"
           << (date.day < 10 ? "0" : "") << date.day;
        return os;
    }
 
    // 输入运算符
    friend istream& operator>>(istream& is, Date& date) {
        char sep;
        is >> date.year >> sep >> date.month >> sep >> date.day;
 
        // 简单的验证
        if (date.month < 1 || date.month > 12 || 
            date.day < 1 || date.day > 31) {
            is.setstate(ios::failbit);
        }
 
        return is;
    }
};
 
int main() {
    Date d1(2024, 6, 15);
    cout << "Date: " << d1 << endl;
 
    Date d2;
    cout << "Enter date (yyyy-mm-dd): ";
    cin >> d2;
 
    if (cin.fail()) {
        cout << "Invalid date format!" << endl;
    } else {
        cout << "You entered: " << d2 << endl;
    }
 
    return 0;
}

7.4 函数调用运算符重载

7.4.1 函数对象(Functor)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
// 函数对象类
class Multiplier {
private:
    int factor;
 
public:
    Multiplier(int f) : factor(f) {}
 
    // 重载函数调用运算符
    int operator()(int value) const {
        return value * factor;
    }
};
 
// 带状态的函数对象
class Average {
private:
    double sum = 0;
    int count = 0;
 
public:
    void operator()(double value) {
        sum += value;
        count++;
    }
 
    double getAverage() const {
        return count > 0 ? sum / count : 0;
    }
};
 
// 比较函数对象
class GreaterThan {
private:
    int threshold;
 
public:
    GreaterThan(int t) : threshold(t) {}
 
    bool operator()(int value) const {
        return value > threshold;
    }
};
 
int main() {
    // 使用函数对象
    Multiplier times3(3);
    cout << "5 * 3 = " << times3(5) << endl;  // 15
    cout << "7 * 3 = " << times3(7) << endl;  // 21
 
    // 在算法中使用
    vector<int> numbers = {1, 2, 3, 4, 5};
    transform(numbers.begin(), numbers.end(), numbers.begin(), Multiplier(10));
 
    cout << "After transform: ";
    for (int n : numbers) cout << n << " ";
    cout << endl;
 
    // 带状态的函数对象
    Average avg;
    avg(10.0);
    avg(20.0);
    avg(30.0);
    cout << "Average: " << avg.getAverage() << endl;
 
    // 查找大于5的元素
    auto it = find_if(numbers.begin(), numbers.end(), GreaterThan(25));
    if (it != numbers.end()) {
        cout << "First > 25: " << *it << endl;
    }
 
    return 0;
}

7.5 类型转换运算符

7.5.1 转换构造函数

#include <iostream>
using namespace std;
 
class Rational {
private:
    int numerator;
    int denominator;
 
public:
    Rational(int n = 0, int d = 1) : numerator(n), denominator(d) {
        if (denominator == 0) denominator = 1;
    }
 
    // 类型转换运算符:Rational -> double
    operator double() const {
        return static_cast<double>(numerator) / denominator;
    }
 
    // 类型转换运算符:Rational -> bool
    explicit operator bool() const {  // explicit防止隐式转换
        return numerator != 0;
    }
 
    // 友元输出
    friend ostream& operator<<(ostream& os, const Rational& r) {
        os << r.numerator << "/" << r.denominator;
        return os;
    }
};
 
int main() {
    Rational r(3, 4);
 
    // 隐式转换为double
    double d = r;  // 调用 operator double()
    cout << r << " = " << d << endl;  // 0.75
 
    // 在算术运算中隐式转换
    double result = r + 0.5;  // r隐式转换为double
    cout << "r + 0.5 = " << result << endl;
 
    // explicit bool需要显式转换
    // bool flag = r;  // 错误!explicit
    bool flag = static_cast<bool>(r);  // 正确
 
    if (r) {  // 条件上下文可以隐式转换
        cout << "r is non-zero" << endl;
    }
 
    return 0;
}

7.5.2 explicit关键字

#include <iostream>
using namespace std;
 
class String {
private:
    char* data;
    size_t length;
 
public:
    // explicit禁止隐式转换
    explicit String(size_t n) {
        length = n;
        data = new char[n + 1]();
        cout << "Created string of length " << n << endl;
    }
 
    String(const char* str) {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
    }
 
    ~String() { delete[] data; }
};
 
void printString(const String& s) {
    cout << "String printed" << endl;
}
 
int main() {
    String s1(100);        // 正确,显式构造
    String s2 = String(100);  // 正确,显式构造
    // String s3 = 100;    // 错误!构造函数是explicit
 
    printString(String(50));  // 正确,显式构造
    // printString(50);      // 错误!不能隐式转换
 
    String s4 = "Hello";   // 正确,String(const char*)不是explicit
    printString("World");  // 正确,隐式转换
 
    return 0;
}

7.6 智能指针运算符

7.6.1 自定义智能指针

#include <iostream>
using namespace std;
 
template<typename T>
class SmartPtr {
private:
    T* ptr;
    size_t* refCount;
 
    void release() {
        if (refCount) {
            (*refCount)--;
            if (*refCount == 0) {
                delete ptr;
                delete refCount;
                cout << "Resource deleted" << endl;
            }
        }
    }
 
public:
    explicit SmartPtr(T* p = nullptr) : ptr(p) {
        refCount = ptr ? new size_t(1) : nullptr;
    }
 
    // 拷贝构造函数
    SmartPtr(const SmartPtr& other) : ptr(other.ptr), refCount(other.refCount) {
        if (refCount) (*refCount)++;
    }
 
    // 拷贝赋值
    SmartPtr& operator=(const SmartPtr& other) {
        if (this != &other) {
            release();
            ptr = other.ptr;
            refCount = other.refCount;
            if (refCount) (*refCount)++;
        }
        return *this;
    }
 
    ~SmartPtr() {
        release();
    }
 
    // 解引用运算符
    T& operator*() const {
        return *ptr;
    }
 
    // 成员访问运算符
    T* operator->() const {
        return ptr;
    }
 
    // 转换为bool
    explicit operator bool() const {
        return ptr != nullptr;
    }
 
    // 获取原始指针
    T* get() const { return ptr; }
 
    // 获取引用计数
    size_t use_count() const {
        return refCount ? *refCount : 0;
    }
};
 
class Test {
public:
    void sayHello() { cout << "Hello!" << endl; }
};
 
int main() {
    SmartPtr<Test> p1(new Test());
    cout << "Ref count: " << p1.use_count() << endl;  // 1
 
    SmartPtr<Test> p2 = p1;
    cout << "Ref count: " << p1.use_count() << endl;  // 2
 
    p1->sayHello();
    (*p2).sayHello();
 
    if (p1) {
        cout << "p1 is valid" << endl;
    }
 
    return 0;
}

7.7 其他运算符

7.7.1 指针访问运算符

#include <iostream>
using namespace std;
 
class Proxy {
private:
    int data;
 
public:
    Proxy(int d) : data(d) {}
    void proxyMethod() { cout << "Proxy method: " << data << endl; }
};
 
class Wrapper {
private:
    Proxy* proxy;
 
public:
    Wrapper(int d) : proxy(new Proxy(d)) {}
    ~Wrapper() { delete proxy; }
 
    // 重载 -> 运算符
    Proxy* operator->() {
        return proxy;
    }
 
    // const版本
    const Proxy* operator->() const {
        return proxy;
    }
};
 
int main() {
    Wrapper w(42);
    w->proxyMethod();  // 等价于 (w.operator->())->proxyMethod()
 
    return 0;
}

7.7.2 new和delete运算符重载

#include <iostream>
#include <cstdlib>
using namespace std;
 
class MemoryTracked {
private:
    static size_t totalAllocated;
    static size_t totalFreed;
 
public:
    // 重载new
    void* operator new(size_t size) {
        totalAllocated += size;
        cout << "Allocating " << size << " bytes" << endl;
        return malloc(size);
    }
 
    // 重载new[]
    void* operator new[](size_t size) {
        totalAllocated += size;
        cout << "Allocating array of " << size << " bytes" << endl;
        return malloc(size);
    }
 
    // 重载delete
    void operator delete(void* ptr, size_t size) {
        totalFreed += size;
        cout << "Deallocating " << size << " bytes" << endl;
        free(ptr);
    }
 
    // 重载delete[]
    void operator delete[](void* ptr, size_t size) {
        totalFreed += size;
        cout << "Deallocating array of " << size << " bytes" << endl;
        free(ptr);
    }
 
    static void printStats() {
        cout << "Total allocated: " << totalAllocated << endl;
        cout << "Total freed: " << totalFreed << endl;
    }
};
 
size_t MemoryTracked::totalAllocated = 0;
size_t MemoryTracked::totalFreed = 0;
 
int main() {
    MemoryTracked* obj = new MemoryTracked();
    delete obj;
 
    MemoryTracked* arr = new MemoryTracked[5];
    delete[] arr;
 
    MemoryTracked::printStats();
 
    return 0;
}

7.8 本章小结

本章我们学习了:

练习题

基础练习:

1. 完善Complex类,实现/、+=、-=、*=、/=运算符

2. 实现一个BigInteger类,支持大整数的基本运算

3. 设计一个Matrix类,实现矩阵的+、-、*运算和[]访问

4. 实现一个安全的数组类,支持边界检查

进阶练习:

5. 实现一个字符串类MyString,支持所有必要的运算符

6. 设计一个二维向量类Vector2D,实现点积、叉积等运算

7. 实现一个日期类Date,支持日期加减和比较运算

思考题:

8. 为什么有些运算符只能作为成员函数重载(如=、[]、→)?

9. 前置++和后置++在效率上有什么区别?

10. 研究C++20的三路比较运算符⇔(太空船运算符)

参考答案

练习3参考:

class Matrix {
private:
    vector<vector<double>> data;
    size_t rows, cols;
 
public:
    Matrix(size_t r, size_t c) : rows(r), cols(c), data(r, vector<double>(c)) {}
 
    double& operator()(size_t i, size_t j) { return data[i][j]; }
    const double& operator()(size_t i, size_t j) const { return data[i][j]; }
 
    Matrix operator+(const Matrix& other) const {
        Matrix result(rows, cols);
        for (size_t i = 0; i < rows; i++)
            for (size_t j = 0; j < cols; j++)
                result(i, j) = (*this)(i, j) + other(i, j);
        return result;
    }
 
    Matrix operator*(const Matrix& other) const {
        Matrix result(rows, other.cols);
        for (size_t i = 0; i < rows; i++)
            for (size_t j = 0; j < other.cols; j++)
                for (size_t k = 0; k < cols; k++)
                    result(i, j) += (*this)(i, k) * other(k, j);
        return result;
    }
};

下一章

继续学习:第八章 模板