C++学习:六个月从基础到就业——面向对象编程:重载运算符(下)

发布于:2025-04-18 ⋅ 阅读:(27) ⋅ 点赞:(0)

C++学习:六个月从基础到就业——面向对象编程:重载运算符(下)

本文是我C++学习之旅系列的第十三篇技术文章,是面向对象编程中运算符重载主题的下篇。本篇文章将继续深入探讨高级运算符重载技术、特殊运算符、常见应用场景和最佳实践。查看完整系列目录了解更多内容。

引言

在上一篇文章中,我们介绍了C++运算符重载的基本概念、语法和常见运算符(如算术、赋值、关系和流运算符等)的重载方法。本篇文章将继续探讨更多高级主题,包括类型转换运算符、内存管理运算符的重载、自定义字面量以及在实际项目中的应用案例。

运算符重载是C++中一个强大而灵活的特性,当合理使用时,可以显著提高代码的可读性和表达能力。然而,过度或不当的使用也可能导致代码难以理解和维护。通过这篇文章,我们将学习如何有效且恰当地利用这一特性,创建直观、安全且高效的代码。

类型转换运算符

类型转换运算符允许我们定义如何将自定义类型转换为其他类型。这些运算符不指定返回类型,因为返回类型已由运算符名称决定。

基本语法

class MyClass {
public:
    // 类型转换运算符语法
    operator TargetType() const;
};

示例:分数类到浮点数的转换

class Fraction {
private:
    int numerator;
    int denominator;
    
public:
    Fraction(int num = 0, int denom = 1) : numerator(num), denominator(denom) {
        if (denominator == 0) throw std::invalid_argument("Denominator cannot be zero");
        normalize();
    }
    
    // 分数转换为double
    operator double() const {
        return static_cast<double>(numerator) / denominator;
    }
    
    // 显示分数
    void display() const {
        std::cout << numerator << "/" << denominator;
    }
    
private:
    // 规范化分数(约分)
    void normalize() {
        if (denominator < 0) {
            numerator = -numerator;
            denominator = -denominator;
        }
        
        int gcd = findGCD(std::abs(numerator), denominator);
        numerator /= gcd;
        denominator /= gcd;
    }
    
    // 求最大公约数
    int findGCD(int a, int b) const {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
};

int main() {
    Fraction f(3, 4);  // 3/4
    
    double d = f;  // 隐式转换:调用operator double()
    std::cout << "Fraction: ";
    f.display();
    std::cout << " as double: " << d << std::endl;
    
    // 在表达式中使用
    double result = f * 2.0;  // f被转换为double,然后乘以2.0
    std::cout << "f * 2.0 = " << result << std::endl;
    
    return 0;
}

显式转换运算符

C++11引入了explicit关键字用于转换运算符,以防止隐式类型转换可能导致的意外行为:

class Fraction {
    // ...前面的代码...
    
    // 显式转换为double,防止意外的隐式转换
    explicit operator double() const {
        return static_cast<double>(numerator) / denominator;
    }
};

int main() {
    Fraction f(3, 4);
    
    // double d = f;  // 错误:不允许隐式转换
    double d = static_cast<double>(f);  // 正确:显式转换
    
    // if (f) { ... }  // 错误:不允许boolean上下文的隐式转换
    if (static_cast<double>(f)) { /* ... */ }  // 正确
    
    return 0;
}

布尔转换运算符

转换为bool类型的运算符特别常见,用于条件判断中:

class SmartPointer {
private:
    int* ptr;
    
public:
    explicit SmartPointer(int* p = nullptr) : ptr(p) {}
    
    ~SmartPointer() { delete ptr; }
    
    // 布尔转换运算符
    explicit operator bool() const {
        return ptr != nullptr;
    }
    
    int& operator*() const {
        if (!ptr) throw std::runtime_error("Null pointer dereference");
        return *ptr;
    }
};

int main() {
    SmartPointer p1(new int(42));
    SmartPointer p2;
    
    if (p1) {  // 使用bool转换运算符
        std::cout << "p1 is valid, value: " << *p1 << std::endl;
    }
    
    if (!p2) {  // 使用bool转换运算符
        std::cout << "p2 is null" << std::endl;
    }
    
    return 0;
}

内存管理运算符重载

new和delete运算符

C++允许重载newdelete运算符,以实现自定义的内存分配和释放策略:

class MemoryTracker {
private:
    static size_t totalAllocated;
    static size_t totalFreed;
    
public:
    // 重载全局new
    void* operator new(size_t size) {
        totalAllocated += size;
        std::cout << "Allocating " << size << " bytes, total: " 
                  << totalAllocated << std::endl;
        return ::operator new(size);  // 调用全局的new
    }
    
    // 重载全局delete
    void operator delete(void* ptr) noexcept {
        ::operator delete(ptr);  // 调用全局的delete
        std::cout << "Memory freed" << std::endl;
    }
    
    // 重载数组new
    void* operator new[](size_t size) {
        totalAllocated += size;
        std::cout << "Allocating array of " << size << " bytes, total: " 
                  << totalAllocated << std::endl;
        return ::operator new[](size);
    }
    
    // 重载数组delete
    void operator delete[](void* ptr) noexcept {
        ::operator delete[](ptr);
        std::cout << "Array memory freed" << std::endl;
    }
    
    // 显示内存使用统计
    static void showStats() {
        std::cout << "Total allocated: " << totalAllocated << " bytes" << std::endl;
        std::cout << "Total freed: " << totalFreed << " bytes" << std::endl;
    }
};

// 初始化静态成员
size_t MemoryTracker::totalAllocated = 0;
size_t MemoryTracker::totalFreed = 0;

int main() {
    MemoryTracker* obj = new MemoryTracker();
    delete obj;
    
    MemoryTracker* arr = new MemoryTracker[5];
    delete[] arr;
    
    MemoryTracker::showStats();
    
    return 0;
}

带placement参数的new运算符

Placement new是一种特殊形式的new运算符,允许在预先分配的内存中构造对象:

class PlacementExample {
private:
    int data;
    
public:
    PlacementExample(int d) : data(d) {
        std::cout << "Constructor called with data = " << data << std::endl;
    }
    
    ~PlacementExample() {
        std::cout << "Destructor called for data = " << data << std::endl;
    }
    
    // 显示数据
    void display() const {
        std::cout << "Data: " << data << std::endl;
    }
    
    // 自定义placement new
    void* operator new(size_t size, char* buffer, const char* name) {
        std::cout << "Placement new called with name: " << name << std::endl;
        return buffer;
    }
    
    // 匹配的placement delete(仅当构造函数抛出异常时调用)
    void operator delete(void* ptr, char* buffer, const char* name) {
        std::cout << "Placement delete called with name: " << name << std::endl;
    }
};

int main() {
    // 预分配内存
    char buffer[sizeof(PlacementExample)];
    
    // 使用placement new在buffer中构造对象
    PlacementExample* obj = new(buffer, "MyObject") PlacementExample(42);
    
    obj->display();
    
    // 显式调用析构函数(不要使用delete,因为内存不是通过new分配的)
    obj->~PlacementExample();
    
    return 0;
}

高级运算符重载技术

自定义字面量(C++11)

C++11引入了用户定义字面量,允许为自定义类型创建特殊的字面量语法:

#include <iostream>
#include <string>
#include <complex>

// 距离字面量
class Distance {
private:
    double meters;
    
public:
    explicit Distance(double m) : meters(m) {}
    
    double getMeters() const { return meters; }
    double getKilometers() const { return meters / 1000.0; }
    double getMiles() const { return meters / 1609.344; }
};

// 用户自定义字面量
Distance operator"" _km(long double km) {
    return Distance(static_cast<double>(km * 1000.0));
}

Distance operator"" _m(long double m) {
    return Distance(static_cast<double>(m));
}

Distance operator"" _mi(long double miles) {
    return Distance(static_cast<double>(miles * 1609.344));
}

// 字符串字面量
std::string operator"" _s(const char* str, size_t len) {
    return std::string(str, len);
}

// 复数字面量(使用标准库复数类)
std::complex<double> operator"" _i(long double val) {
    return std::complex<double>(0.0, static_cast<double>(val));
}

int main() {
    auto dist1 = 5.0_km;
    auto dist2 = 100.0_m;
    auto dist3 = 3.5_mi;
    
    std::cout << "Distance in kilometers: " << dist1.getKilometers() << " km" << std::endl;
    std::cout << "Distance in meters: " << dist2.getMeters() << " m" << std::endl;
    std::cout << "Distance in miles: " << dist3.getMiles() << " mi" << std::endl;
    std::cout << "Distance in meters: " << dist3.getMeters() << " m" << std::endl;
    
    auto name = "John Doe"_s;
    std::cout << "Name: " << name << ", length: " << name.length() << std::endl;
    
    auto c = 2.0 + 3.0_i;
    std::cout << "Complex number: " << c << std::endl;
    std::cout << "Real part: " << c.real() << ", Imaginary part: " << c.imag() << std::endl;
    
    return 0;
}

重载逗号运算符

逗号运算符可以重载,但在实际应用中很少使用,因为它可能导致代码难以理解:

class Sequence {
private:
    std::vector<int> data;
    
public:
    // 重载逗号运算符以创建序列
    Sequence& operator,(int value) {
        data.push_back(value);
        return *this;
    }
    
    // 显示序列内容
    void display() const {
        std::cout << "Sequence: ";
        for (int val : data) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    Sequence seq;
    
    // 使用逗号运算符构建序列
    seq, 1, 2, 3, 4, 5;
    
    seq.display();  // 输出: Sequence: 1 2 3 4 5
    
    return 0;
}

虽然这是可行的,但这种用法不直观且可能令人困惑,通常应避免重载逗号运算符。

智能引用(代理类)模式

有时我们需要在访问某个元素时执行额外的逻辑。这可以通过返回一个"代理"对象而不是直接引用来实现:

class BoundsCheckedArray {
private:
    int* data;
    size_t size;
    
    // 代理类,用于[]运算符的返回值
    class Reference {
    private:
        BoundsCheckedArray& array;
        size_t index;
        
    public:
        Reference(BoundsCheckedArray& a, size_t idx) : array(a), index(idx) {}
        
        // 转换为int,允许读取值
        operator int() const {
            std::cout << "Reading element at index " << index << std::endl;
            return array.data[index];
        }
        
        // 赋值运算符,允许修改值
        Reference& operator=(int value) {
            std::cout << "Writing value " << value << " to index " << index << std::endl;
            array.data[index] = value;
            return *this;
        }
    };
    
public:
    BoundsCheckedArray(size_t s) : size(s) {
        data = new int[size]();
    }
    
    ~BoundsCheckedArray() {
        delete[] data;
    }
    
    // 下标运算符返回Reference对象而非int&
    Reference operator[](size_t index) {
        if (index >= size) {
            throw std::out_of_range("Index out of bounds");
        }
        return Reference(*this, index);
    }
    
    size_t getSize() const { return size; }
};

int main() {
    BoundsCheckedArray arr(5);
    
    // 写入值
    arr[0] = 10;
    arr[1] = 20;
    
    // 读取值
    int val = arr[0];
    std::cout << "Value at index 0: " << val << std::endl;
    
    // 读取并修改
    arr[2] = arr[1] * 2;
    std::cout << "Value at index 2: " << arr[2] << std::endl;
    
    try {
        arr[10] = 100;  // 会抛出异常
    } catch (const std::exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
    }
    
    return 0;
}

这种模式在STL的std::vector<bool>中使用,因为它并不真正存储布尔值,而是以位形式压缩存储。

自定义索引

下标运算符不限于使用整数索引,我们可以使用任何类型作为索引:

class Dictionary {
private:
    std::map<std::string, std::string> entries;
    
public:
    // 使用字符串作为索引
    std::string& operator[](const std::string& key) {
        return entries[key];
    }
    
    // const版本
    const std::string& operator[](const std::string& key) const {
        auto it = entries.find(key);
        if (it == entries.end()) {
            static const std::string empty_string;
            return empty_string;
        }
        return it->second;
    }
    
    // 检查键是否存在
    bool contains(const std::string& key) const {
        return entries.find(key) != entries.end();
    }
    
    // 获取条目数量
    size_t size() const {
        return entries.size();
    }
};

int main() {
    Dictionary dict;
    
    dict["apple"] = "A fruit with red or green skin and crisp flesh";
    dict["banana"] = "A long curved fruit with a yellow skin";
    dict["cherry"] = "A small, round stone fruit that is typically bright or dark red";
    
    std::cout << "Apple: " << dict["apple"] << std::endl;
    std::cout << "Banana: " << dict["banana"] << std::endl;
    std::cout << "Unknown: " << dict["unknown"] << std::endl;  // 返回空字符串
    
    std::cout << "Number of entries: " << dict.size() << std::endl;
    
    return 0;
}

多维下标运算符

我们可以链式调用下标运算符来实现多维索引:

class Matrix {
private:
    std::vector<std::vector<double>> data;
    size_t rows, cols;
    
    // 代理类,用于实现第二维索引
    class RowProxy {
    private:
        std::vector<double>& row;
        
    public:
        RowProxy(std::vector<double>& r) : row(r) {}
        
        // 返回特定元素的引用
        double& operator[](size_t col) {
            return row[col];
        }
        
        // const版本
        const double& operator[](size_t col) const {
            return row[col];
        }
    };
    
public:
    Matrix(size_t r, size_t c) : rows(r), cols(c) {
        data.resize(rows);
        for (auto& row : data) {
            row.resize(cols, 0.0);
        }
    }
    
    // 返回代理对象,用于实现第二维索引
    RowProxy operator[](size_t row) {
        return RowProxy(data[row]);
    }
    
    // const版本
    const RowProxy operator[](size_t row) const {
        return RowProxy(const_cast<std::vector<double>&>(data[row]));
    }
    
    // 获取尺寸
    size_t getRows() const { return rows; }
    size_t getCols() const { return cols; }
};

int main() {
    Matrix m(3, 3);
    
    // 初始化矩阵
    for (size_t i = 0; i < m.getRows(); ++i) {
        for (size_t j = 0; j < m.getCols(); ++j) {
            m[i][j] = i * m.getCols() + j + 1;
        }
    }
    
    // 显示矩阵
    for (size_t i = 0; i < m.getRows(); ++i) {
        for (size_t j = 0; j < m.getCols(); ++j) {
            std::cout << m[i][j] << "\t";
        }
        std::cout << std::endl;
    }
    
    return 0;
}

实际应用案例

大整数类

以下是一个更完整的大整数类实现,展示了多种运算符重载的应用:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>

class BigInteger {
private:
    std::vector<int> digits;  // 每个元素存储一位数字,低位在前
    bool negative;            // 符号标志
    
    // 移除前导零
    void removeLeadingZeros() {
        while (digits.size() > 1 && digits.back() == 0) {
            digits.pop_back();
        }
        
        // 特殊情况:零应该是正数
        if (digits.size() == 1 && digits[0] == 0) {
            negative = false;
        }
    }
    
    // 比较绝对值
    int compareAbs(const BigInteger& other) const {
        if (digits.size() != other.digits.size()) {
            return digits.size() > other.digits.size() ? 1 : -1;
        }
        
        for (int i = digits.size() - 1; i >= 0; --i) {
            if (digits[i] != other.digits[i]) {
                return digits[i] > other.digits[i] ? 1 : -1;
            }
        }
        
        return 0;  // 相等
    }
    
    // 加法实现(不考虑符号)
    BigInteger addAbs(const BigInteger& other) const {
        BigInteger result;
        result.digits.clear();
        result.negative = false;
        
        int carry = 0;
        size_t i = 0, j = 0;
        
        while (i < digits.size() || j < other.digits.size() || carry) {
            int sum = carry;
            if (i < digits.size()) sum += digits[i++];
            if (j < other.digits.size()) sum += other.digits[j++];
            
            result.digits.push_back(sum % 10);
            carry = sum / 10;
        }
        
        return result;
    }
    
    // 减法实现(不考虑符号,假设this >= other)
    BigInteger subAbs(const BigInteger& other) const {
        BigInteger result;
        result.digits.clear();
        result.negative = false;
        
        int borrow = 0;
        size_t i = 0, j = 0;
        
        while (i < digits.size()) {
            int diff = digits[i++] - borrow;
            if (j < other.digits.size()) diff -= other.digits[j++];
            
            if (diff < 0) {
                diff += 10;
                borrow = 1;
            } else {
                borrow = 0;
            }
            
            result.digits.push_back(diff);
        }
        
        result.removeLeadingZeros();
        return result;
    }
    
    // 乘法实现(不考虑符号)
    BigInteger mulAbs(const BigInteger& other) const {
        // 特殊情况处理:如果任一数为0,则结果为0
        if ((digits.size() == 1 && digits[0] == 0) || 
            (other.digits.size() == 1 && other.digits[0] == 0)) {
            return BigInteger("0");
        }
        
        // 初始化结果为0,长度为两数之和
        std::vector<int> result(digits.size() + other.digits.size(), 0);
        
        // 执行乘法
        for (size_t i = 0; i < digits.size(); ++i) {
            int carry = 0;
            for (size_t j = 0; j < other.digits.size() || carry; ++j) {
                int current = result[i + j] + digits[i] * (j < other.digits.size() ? other.digits[j] : 0) + carry;
                result[i + j] = current % 10;
                carry = current / 10;
            }
        }
        
        // 构造结果
        BigInteger product;
        product.digits = result;
        product.removeLeadingZeros();
        return product;
    }
    
public:
    // 构造函数
    BigInteger() : negative(false) {
        digits.push_back(0);  // 默认值为0
    }
    
    BigInteger(long long num) : negative(num < 0) {
        num = std::abs(num);
        
        if (num == 0) {
            digits.push_back(0);
        } else {
            while (num > 0) {
                digits.push_back(num % 10);
                num /= 10;
            }
        }
    }
    
    BigInteger(const std::string& str) : negative(false) {
        if (str.empty()) {
            digits.push_back(0);
            return;
        }
        
        size_t start = 0;
        if (str[0] == '-') {
            negative = true;
            start = 1;
        } else if (str[0] == '+') {
            start = 1;
        }
        
        // 逆序添加数字(低位在前)
        for (int i = str.length() - 1; i >= static_cast<int>(start); --i) {
            if (!std::isdigit(str[i])) {
                throw std::invalid_argument("Invalid character in string");
            }
            digits.push_back(str[i] - '0');
        }
        
        removeLeadingZeros();
    }
    
    // 一元加号运算符
    BigInteger operator+() const {
        return *this;
    }
    
    // 一元减号运算符
    BigInteger operator-() const {
        BigInteger result = *this;
        // 零的符号始终为正
        if (!(result.digits.size() == 1 && result.digits[0] == 0)) {
            result.negative = !result.negative;
        }
        return result;
    }
    
    // 加法运算符
    BigInteger operator+(const BigInteger& other) const {
        // 如果符号相同
        if (negative == other.negative) {
            BigInteger result = addAbs(other);
            result.negative = negative;
            return result;
        }
        
        // 符号不同:|a| >= |b|时,结果是a的符号;否则是b的符号
        int cmp = compareAbs(other);
        if (cmp == 0) {
            return BigInteger();  // 结果为0
        } else if (cmp > 0) {
            BigInteger result = subAbs(other);
            result.negative = negative;
            return result;
        } else {
            BigInteger result = other.subAbs(*this);
            result.negative = other.negative;
            return result;
        }
    }
    
    // 减法运算符
    BigInteger operator-(const BigInteger& other) const {
        return *this + (-other);
    }
    
    // 乘法运算符
    BigInteger operator*(const BigInteger& other) const {
        BigInteger result = mulAbs(other);
        // 异号得负,同号得正
        result.negative = (negative != other.negative) && !(result.digits.size() == 1 && result.digits[0] == 0);
        return result;
    }
    
    // 复合赋值运算符
    BigInteger& operator+=(const BigInteger& other) {
        *this = *this + other;
        return *this;
    }
    
    BigInteger& operator-=(const BigInteger& other) {
        *this = *this - other;
        return *this;
    }
    
    BigInteger& operator*=(const BigInteger& other) {
        *this = *this * other;
        return *this;
    }
    
    // 关系运算符
    bool operator==(const BigInteger& other) const {
        return negative == other.negative && digits == other.digits;
    }
    
    bool operator!=(const BigInteger& other) const {
        return !(*this == other);
    }
    
    bool operator<(const BigInteger& other) const {
        // 符号不同
        if (negative != other.negative) {
            return negative;
        }
        
        // 符号相同,比较绝对值
        int cmp = compareAbs(other);
        return negative ? cmp > 0 : cmp < 0;
    }
    
    bool operator>(const BigInteger& other) const {
        return other < *this;
    }
    
    bool operator<=(const BigInteger& other) const {
        return !(other < *this);
    }
    
    bool operator>=(const BigInteger& other) const {
        return !(*this < other);
    }
    
    // 输入输出运算符
    friend std::ostream& operator<<(std::ostream& os, const BigInteger& num);
    friend std::istream& operator>>(std::istream& is, BigInteger& num);
    
    // 转换为字符串
    std::string toString() const {
        if (digits.size() == 1 && digits[0] == 0) {
            return "0";
        }
        
        std::string result;
        if (negative) {
            result += "-";
        }
        
        for (int i = digits.size() - 1; i >= 0; --i) {
            result += static_cast<char>(digits[i] + '0');
        }
        
        return result;
    }
};

// 输出运算符
std::ostream& operator<<(std::ostream& os, const BigInteger& num) {
    os << num.toString();
    return os;
}

// 输入运算符
std::istream& operator>>(std::istream& is, BigInteger& num) {
    std::string input;
    is >> input;
    try {
        num = BigInteger(input);
    } catch (const std::invalid_argument& e) {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

int main() {
    BigInteger a("123456789012345678901234567890");
    BigInteger b("987654321098765432109876543210");
    BigInteger c = -a;
    
    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "c = " << c << std::endl;
    std::cout << "a + b = " << (a + b) << std::endl;
    std::cout << "a - b = " << (a - b) << std::endl;
    std::cout << "a * b = " << (a * b) << std::endl;
    std::cout << "a < b? " << std::boolalpha << (a < b) << std::endl;
    std::cout << "a > b? " << (a > b) << std::endl;
    std::cout << "a == b? " << (a == b) << std::endl;
    std::cout << "a != b? " << (a != b) << std::endl;
    
    BigInteger d("0");
    std::cout << "-d = " << -d << std::endl;  // 应该仍然是0
    
    std::cout << "请输入一个大整数: ";
    BigInteger input;
    if (std::cin >> input) {
        std::cout << "您输入的数字是: " << input << std::endl;
        std::cout << "input + a = " << (input + a) << std::endl;
    } else {
        std::cout << "输入格式错误!" << std::endl;
    }
    
    return 0;
}

日期类

以下是一个日期类的实现,展示了各种运算符的重载:

#include <iostream>
#include <iomanip>
#include <string>
#include <stdexcept>

class Date {
private:
    int year;
    int month;
    int day;
    
    // 检查日期是否有效
    bool isValid() const {
        if (year < 1 || month < 1 || month > 12 || day < 1) {
            return false;
        }
        
        const int daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        int maxDay = daysInMonth[month];
        
        // 二月闰年判断
        if (month == 2 && isLeapYear()) {
            maxDay = 29;
        }
        
        return day <= maxDay;
    }
    
    // 判断闰年
    bool isLeapYear() const {
        return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    }
    
    // 计算从公元元年1月1日开始的总天数
    int toDays() const {
        int days = (year - 1) * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400;
        
        const int daysBeforeMonth[] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
        days += daysBeforeMonth[month];
        
        // 如果当前年是闰年且已经过了2月
        if (month > 2 && isLeapYear()) {
            days++;
        }
        
        days += day;
        return days;
    }
    
    // 从总天数设置日期
    void fromDays(int days) {
        // 估算年份
        year = days / 365;
        int leapDays = year / 4 - year / 100 + year / 400;
        
        while (days <= leapDays + (year - 1) * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400) {
            year--;
            leapDays = year / 4 - year / 100 + year / 400;
        }
        
        while (days > leapDays + year * 365 + year / 4 - year / 100 + year / 400) {
            year++;
            leapDays = year / 4 - year / 100 + year / 400;
        }
        
        // 计算日期
        int daysInYear = days - ((year - 1) * 365 + (year - 1) / 4 - (year - 1) / 100 + (year - 1) / 400);
        
        const int daysBeforeMonth[] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
        bool isLeap = isLeapYear();
        
        month = 1;
        while (month < 12 && daysInYear > daysBeforeMonth[month + 1] + (isLeap && month >= 2 ? 1 : 0)) {
            month++;
        }
        
        day = daysInYear - daysBeforeMonth[month] - (isLeap && month > 2 ? 1 : 0);
    }
    
public:
    // 构造函数
    Date(int y, int m, int d) : year(y), month(m), day(d) {
        if (!isValid()) {
            throw std::invalid_argument("Invalid date");
        }
    }
    
    // 获取年、月、日
    int getYear() const { return year; }
    int getMonth() const { return month; }
    int getDay() const { return day; }
    
    // 加减天数
    Date operator+(int days) const {
        int totalDays = toDays() + days;
        Date result(1, 1, 1);  // 临时日期
        result.fromDays(totalDays);
        return result;
    }
    
    Date operator-(int days) const {
        return *this + (-days);
    }
    
    // 日期差值
    int operator-(const Date& other) const {
        return toDays() - other.toDays();
    }
    
    // 复合赋值
    Date& operator+=(int days) {
        *this = *this + days;
        return *this;
    }
    
    Date& operator-=(int days) {
        *this = *this - days;
        return *this;
    }
    
    // 递增递减
    Date& operator++() {  // 前缀
        *this += 1;
        return *this;
    }
    
    Date operator++(int) {  // 后缀
        Date temp = *this;
        *this += 1;
        return temp;
    }
    
    Date& operator--() {  // 前缀
        *this -= 1;
        return *this;
    }
    
    Date operator--(int) {  // 后缀
        Date temp = *this;
        *this -= 1;
        return temp;
    }
    
    // 比较运算符
    bool operator==(const Date& other) const {
        return year == other.year && month == other.month && day == other.day;
    }
    
    bool operator!=(const Date& other) const {
        return !(*this == other);
    }
    
    bool operator<(const Date& other) const {
        if (year != other.year) return year < other.year;
        if (month != other.month) return month < other.month;
        return day < other.day;
    }
    
    bool operator>(const Date& other) const {
        return other < *this;
    }
    
    bool operator<=(const Date& other) const {
        return !(*this > other);
    }
    
    bool operator>=(const Date& other) const {
        return !(*this < other);
    }
    
    // 输入输出
    friend std::ostream& operator<<(std::ostream& os, const Date& date);
    friend std::istream& operator>>(std::istream& is, Date& date);
};

// 输出运算符
std::ostream& operator<<(std::ostream& os, const Date& date) {
    os << std::setw(4) << std::setfill('0') << date.year << '-'
       << std::setw(2) << std::setfill('0') << date.month << '-'
       << std::setw(2) << std::setfill('0') << date.day;
    return os;
}

// 输入运算符
std::istream& operator>>(std::istream& is, Date& date) {
    char dash1, dash2;
    int y, m, d;
    
    is >> y >> dash1 >> m >> dash2 >> d;
    
    if (is && dash1 == '-' && dash2 == '-') {
        try {
            date = Date(y, m, d);
        } catch (const std::invalid_argument&) {
            is.setstate(std::ios_base::failbit);
        }
    } else {
        is.setstate(std::ios_base::failbit);
    }
    
    return is;
}

int main() {
    try {
        Date today(2023, 10, 28);
        Date tomorrow = today + 1;
        Date yesterday = today - 1;
        
        std::cout << "Today: " << today << std::endl;
        std::cout << "Tomorrow: " << tomorrow << std::endl;
        std::cout << "Yesterday: " << yesterday << std::endl;
        
        std::cout << "Days between tomorrow and yesterday: " << (tomorrow - yesterday) << std::endl;
        
        Date nextWeek = today;
        nextWeek += 7;
        std::cout << "Next week: " << nextWeek << std::endl;
        
        // 递增测试
        Date d = today;
        std::cout << "d++ = " << d++ << std::endl;
        std::cout << "After d++: " << d << std::endl;
        std::cout << "++d = " << ++d << std::endl;
        
        // 比较测试
        std::cout << "today == tomorrow? " << std::boolalpha << (today == tomorrow) << std::endl;
        std::cout << "today < tomorrow? " << (today < tomorrow) << std::endl;
        std::cout << "today > yesterday? " << (today > yesterday) << std::endl;
        
        std::cout << "请输入日期 (YYYY-MM-DD): ";
        Date input(2000, 1, 1);
        if (std::cin >> input) {
            std::cout << "您输入的日期是: " << input << std::endl;
            std::cout << "距离今天的天数: " << (input - today) << std::endl;
        } else {
            std::cout << "输入格式错误!" << std::endl;
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

最佳实践与陷阱

运算符重载的最佳实践

  1. 保持语义直观:重载运算符应该与其原始语义保持一致。例如,+应该表示加法操作,而不是其他无关的功能。

  2. 成对实现相关运算符:如果实现了==,也应实现!=;如果实现了<,考虑实现所有比较运算符。

  3. 避免意外的类型转换:使用explicit关键字防止意外的隐式类型转换。

  4. 保证一致的返回类型:运算符的返回类型应与其语义匹配。例如,赋值运算符应返回对象的引用,比较运算符应返回布尔值。

  5. 考虑链式操作:复合赋值运算符应该返回*this的引用,以支持链式操作。

  6. 基于复合赋值运算符实现二元运算符:这样可以减少代码重复。

  7. 关注效率:特别是对于大型对象,应该避免不必要的对象复制。

  8. 使用友元函数适当地:尤其是对于需要转换左操作数的情况。

常见陷阱

  1. 修改了运算符的预期行为
// 错误示例
class BadIdea {
public:
    bool operator+(const BadIdea&) const {
        return true;  // 加法运算符返回布尔值?
    }
    
    void operator-(const BadIdea&) const {
        std::cout << "Subtraction" << std::endl;  // 减法运算符无返回值?
    }
};
  1. 不正确处理自赋值
class Resource {
public:
    Resource& operator=(const Resource& other) {
        // 错误:未检查自赋值
        delete[] data;  // 如果this == &other,这会删除正在尝试复制的数据
        data = new int[other.size];
        std::copy(other.data, other.data + other.size, data);
        return *this;
    }
    
private:
    int* data;
    size_t size;
};
  1. 破坏不变式
class Fraction {
public:
    // 错误:不保证分数格式的有效性
    Fraction& operator+=(const Fraction& other) {
        numerator = numerator * other.denominator + other.numerator * denominator;
        denominator = denominator * other.denominator;
        // 缺少对分数的规范化(约分)
        return *this;
    }
    
private:
    int numerator;
    int denominator;
};
  1. 忽略边界情况
class SafeDivision {
public:
    // 错误:没有处理除以零的情况
    double operator/(double divisor) const {
        // 应该检查divisor是否为零
        return value / divisor;
    }
    
private:
    double value;
};
  1. 重载逻辑运算符
class LogicalOverload {
public:
    // 错误:重载&&和||会失去短路评估特性
    bool operator&&(const LogicalOverload& other) const {
        return value && other.value;
    }
    
    bool operator||(const LogicalOverload& other) const {
        return value || other.value;
    }
    
private:
    bool value;
};
  1. 过度重载
// 错误:为类重载过多不必要的运算符
class OverloadEverything {
public:
    OverloadEverything operator+(const OverloadEverything&) const;
    OverloadEverything operator-(const OverloadEverything&) const;
    OverloadEverything operator*(const OverloadEverything&) const;
    OverloadEverything operator/(const OverloadEverything&) const;
    OverloadEverything operator%(const OverloadEverything&) const;
    OverloadEverything operator^(const OverloadEverything&) const;
    OverloadEverything operator&(const OverloadEverything&) const;
    OverloadEverything operator|(const OverloadEverything&) const;
    // ... 更多不必要的运算符
};

总结

运算符重载是C++面向对象编程中一个强大而灵活的特性,它允许我们为自定义类型定义标准运算符的行为,创建直观且易于使用的接口。通过合理地使用运算符重载,我们可以使得代码更加自然、可读,并且保持与内置类型一致的使用方式。

本文介绍了多种高级运算符重载技术和应用场景,包括类型转换运算符、内存管理运算符、自定义字面量、代理类模式等。我们还通过两个完整的实际应用案例——大整数类和日期类,展示了如何在实践中应用这些技术。

然而,运算符重载也需要谨慎使用。遵循最佳实践并避免常见陷阱,是确保代码质量和可维护性的关键。特别是,我们应该始终保持运算符的语义直观性,避免违反用户的预期,并确保运算符的行为与其原始含义一致。

随着C++标准的发展,运算符重载的应用也在不断扩展。从C++11引入的移动语义和自定义字面量,到C++20引入的三路比较运算符(<=>),这些都为我们提供了更多表达力和灵活性。

在下一篇文章中,我们将探讨C++面向对象编程的另一个重要主题:虚函数与抽象类,这是多态性的核心机制。


这是我C++学习之旅系列的第十三篇技术文章。查看完整系列目录了解更多内容。


网站公告

今日签到

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