C++重载运算符

常用运算符:C/C++运算符优先级

operator()

operator() 在 C++ 中有两个主要作用:

重载 ()操作符 (Overloading)

  • 函数对象(Functors): 当一个类重载了 () 操作符时,它的对象可以像函数一样被调用。这样的类对象被称为函数对象。

  • 灵活性: 与普通函数相比,函数对象更为灵活,代码也更加优雅。

  • 状态共享: 函数对象可以有自己的状态变量,使得在多次调用过程中可以共享这些状态。

  • 独特的类型: 函数对象有其特有的类型,可以通过传递相应的类型作为参数来实例化特定的模块,例如带参数的函数形参。

#include <iostream>

class Adder {
public:
Adder(int n) : total(n) {}

// 重载 () 操作符
int operator()(int value) {
total += value;
return total;
}

private:
int total;
};

int main() {
Adder add(10); // 创建一个 Adder 对象,初始值为 10

std::cout << add(5) << std::endl; // 输出 15
std::cout << add(10) << std::endl; // 输出 25

return 0;
}

实现对象类型转换 (Casting)

  • 在 C++ 中,可以通过 operator Type() 的形式定义类型转换函数,从而将类对象转换为指定的 Type 类型。

#include <iostream>

class Integer {
public:
Integer(int n) : value(n) {}

// 定义类型转换函数,将 Integer 对象转换为 int 类型
operator int() const {
return value;
}

private:
int value;
};

int main() {
Integer obj(42); // 创建一个 Integer 对象

int num = obj; // 通过类型转换函数将对象转换为 int 类型
std::cout << num << std::endl; // 输出 42

return 0;
}

不能重载的运算符

运算符 描述 不能重载的原因
&& 和 ` `
?: 条件运算符 固有语法,语法层面与控制流结构绑定,重载会影响逻辑清晰度和代码可读性。
, 逗号运算符 重载后无法保证左侧表达式先于右侧表达式求值,破坏顺序求值语义。
:: 作用域解析运算符 直接影响作用域解析机制,重载会破坏语言的基本结构,影响编译器解析符号的能力。
. 成员访问运算符 影响对象的成员访问机制,破坏类的封装性和对象模型。
.* 成员指针访问运算符 重载会复杂化成员指针解析,影响代码的可读性和一致性。
typeid, const_cast, dynamic_cast, reinterpret_cast, static_cast 类型转换运算符 重载会破坏类型安全性,导致代码不稳定。
sizeof 获取类型或对象大小 与内存布局和编译器行为直接相关,重载会导致内存管理和优化过程复杂化。
alignof 获取类型的对齐要求 与内存布局相关,不能重载。
typeid 获取类型信息 属于RTTI机制的一部分,重载会影响类型信息获取,破坏RTTI机制。
new, delete, new[], delete[] 内存分配和释放 关键字 newdelete 的行为是固定的,尽管可以重载 operator newoperator delete,但不能重载关键字本身的行为。

运算符重载

  1. 保持运算符语义:在重载运算符时,应保留该运算符作用于基本数据类型时的特性和语义。例如:

    • ++=:应体现加法和复合赋值操作的语义。
    • ++--:区分前置和后置操作符,前置返回自增/自减后的值,后置返回自增/自减前的值。
  2. 运算符重载形式:运算符重载可以作为类的成员函数或友元函数来实现:

    • 成员函数:适用于操作数个数减少的情况。形参个数等于操作数个数减1(++-- 的后置版本除外)。
    • 友元函数:适用于需要访问类的私有成员的情况。形参个数等于操作数个数(++-- 的后置版本除外)。

重载成员函数

  1. 改变对象状态:通常涉及改变对象内部状态的运算符(如自增、自减、赋值)应该定义为成员函数。这是因为成员函数可以直接访问对象的私有成员并修改其状态。

  2. 必须定义为成员的运算符

    赋值运算符 (=)、下标运算符 ([])、函数调用运算符 (())、成员访问箭头运算符 (->) 必须定义为类的成员函数,否则会出现编译错误。

    class MyClass {
    public:
    int value;

    // 构造函数
    MyClass(int v) : value(v) {}

    // 重载 += 运算符(成员函数)
    MyClass& operator+=(const MyClass& rhs) {
    value += rhs.value;
    return *this;
    }

    // 重载前置 ++ 运算符(成员函数)
    MyClass& operator++() {
    ++value;
    return *this;
    }

    // 重载后置 ++ 运算符(成员函数)
    MyClass operator++(int) {
    MyClass temp = *this;
    ++value;
    return temp;
    }
    };

重载友元函数

  1. 访问私有成员:如果需要在类外访问对象的私有成员,可以将运算符重载为友元函数。

  2. 避免对象调用的依赖:如果希望运算符在不依赖对象调用的情况下被使用,如 a + b,友元函数是更好的选择。

  3. 类型兼容:如果运算符需要处理不同类型的操作数,如 int + class 的形式,则必须将运算符定义为全局的友元函数。

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

    // 友元函数重载 +
    friend Complex operator+(const Complex& lhs, const Complex& rhs);
    friend Complex operator+(double lhs, const Complex& rhs);

    private:
    double real;
    double imag;
    };

    Complex operator+(const Complex& lhs, const Complex& rhs) {
    return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
    }

    Complex operator+(double lhs, const Complex& rhs) {
    return Complex(lhs + rhs.real, rhs.imag);
    }

指导原则

  1. 必须定义为成员的运算符

    • 赋值运算符 (=)、下标运算符 ([])、函数调用运算符 (())、成员访问箭头运算符 (->) 必须定义为类的成员函数。
  2. 通常定义为成员的运算符

    • 改变对象状态的运算符,如自增 (++)、自减 (--)、解引用 (*),通常定义为成员函数。
  3. 通常定义为友元的运算符

    • 对称运算符,如算术运算符(+- 等)、相等运算符(==!=)、关系运算符(<> 等)更适合定义为友元函数,因为它们通常涉及两个不同类型的操作数。
    • 输入输出运算符 (<<>>) 也应定义为友元函数,以便处理类与流对象的交互。
    class Complex {
    public:
    Complex(double r, double i) : real(r), imag(i) {}

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

    private:
    double real;
    double imag;
    };

    std::ostream& operator<<(std::ostream& os, const Complex& c) {
    os << "(" << c.real << ", " << c.imag << ")";
    return os;
    }
class MyClass {
public:
int value;

// 构造函数
MyClass(int v) : value(v) {}

// 重载 + 运算符(友元函数)
friend MyClass operator+(const MyClass& lhs, const MyClass& rhs) {
return MyClass(lhs.value + rhs.value);
}

// 重载 += 运算符(成员函数)
MyClass& operator+=(const MyClass& rhs) {
value += rhs.value;
return *this;
}

// 重载前置 ++ 运算符(成员函数)
MyClass& operator++() {
++value;
return *this;
}

// 重载后置 ++ 运算符(成员函数)
MyClass operator++(int) {
MyClass temp = *this;
++value;
return temp;
}
};
------------------------------- 本文结束啦❤感谢您阅读-------------------------------
赞赏一杯咖啡