当前位置: 首页 > news >正文

[C++]运算符重载

一、 什么是运算符重载?

运算符重载是 C++ 中的一种功能,它允许用户定义的类或数据类型重新定义或扩展运算符的行为,使运算符能够作用于用户定义的对象。


二、 通俗解释

在 C++ 中,运算符(如 +, -, *, = 等)默认只能对内置类型(如 int, float)进行操作。
运算符重载允许用户通过特殊的函数定义,让运算符也能处理自定义类型(如类或结构体)。

例如:

  • 默认情况下,+ 只能用来加两个整数:3 + 5
  • 运算符重载后,+ 可以用来加两个对象(如 ComplexNumber 类对象)。

三、运算符重载的目的

  • 让自定义类型(如类和结构体)与内置类型一样,可以使用运算符进行运算或比较。
  • 使代码更简洁直观,提高程序的可读性和可维护性。

四、运算符重载的规则

  1. 不能重载的运算符:

    • ::(作用域运算符)
    • .*(成员指针运算符)
    • sizeof(计算对象大小)
    • typeid(类型识别运算符)
    • .(成员访问运算符)
  2. 运算符重载不会改变运算符的优先级和结合性

  3. 运算符重载不能引入新的运算符,只能重载已有的运算符。

  4. 不能改变运算符的操作数个数

    • 单目运算符:例如 -(负号)。
    • 双目运算符:例如 +, *, ==
  5. 运算符重载可以作为类的成员函数或友元函数,具体情况如下:

    • 成员函数:左操作数必须是当前类对象。
    • 友元函数:适用于左操作数不是当前类对象的情况。

五、重载运算符

运算符重载是通过特殊的函数实现的,这个函数名以 operator 开头,后面跟要重载的运算符。

1.运算符重载的实现方式

1.1 运算符重载作为类的成员函数

1.1.1语法格式:
ReturnType operator 运算符 (参数列表) {//ReturnType是运算符重载针对的数据类型// 重载运算符的逻辑
}
1.1.2 特点:
  • 左侧操作数必须是当前类的对象。(关于左操作数的解释下面会有具体的扩展哈)
  • 右侧操作数通过参数传递(如果是双目运算符)。
1.1.3 代码实例:重载 + 运算符(成员函数):计算两个复数的加法结果,并返回一个新的复数对象。(复数用对象表示,在对象的内部将复数的虚部和实部用成员变量来表示,即这里的一个Complex对象实例就是一个复数,这个对象里的real就是这个对象的实部,imag就是这个对象的虚部)
#include <iostream>
using namespace std;class Complex {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);
//这里的other.real指的是其他的要跟当下的对象的real相加的实部(这里代码可以看做real是c1的虚部,other.real是c2的虚部)
//这里的other.imag指的是其他的要跟当下的对象的imag相加的实部(这里代码可以看做imag是c1的虚部,other.imag是c2的虚部)}void display() {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(3, 4), c2(1, 2);Complex c3 = c1 + c2;  // 调用重载的 + 运算符//创建了一个新的对象c3,利用Complex对象+重载运算符将c1,c2对象的虚部和实部对应的相加给c3(c1,c2的实部相加给c3的实部,c1,c2的虚部相加给c3的虚部)c3.display();return 0;
}
1.1.4 解释:
1.other 是什么?

在运算符重载函数中:

Complex operator+(const Complex& other) {return Complex(real + other.real, imag + other.imag);
}
  •  other 是重载 + 运算符函数的 参数
  • 它代表 + 运算符右侧的对象

例如:

Complex c1(3, 4), c2(1, 2);
Complex c3 = c1 + c2;

 在表达式 c1 + c2 中:

  • c1 是左操作数,它是调用 operator+ 的对象(this 指针隐式传递)。
  • c2 是右操作数,它作为参数 other 传递给 operator+ 函数。
2. other.realother.imag 是什么?

other 是一个 Complex 类型的对象,它具有 realimag 两个成员变量(实部和虚部)。

  • other.real:表示 other 对象的 实部
  • other.imag:表示 other 对象的 虚部
3.执行流程解析

1.创建对象 c1c2

Complex c1(3, 4);
Complex c2(1, 2);
  • c1real = 3, imag = 4

  • c2real = 1, imag = 2

2.调用 c1 + c2:

Complex c3 = c1 + c2;
  •  这行代码触发了 运算符重载函数 operator+
  • 在函数 operator+ 中:
    • this->realthis->imag 代表左操作数 c1 的实部和虚部。
    • other 是右操作数 c2
    • other.realother.imag 代表 c2 的实部和虚部。

3.具体计算过程:

return Complex(real + other.real, imag + other.imag);
  •  real + other.real3 + 1 = 4(计算新的实部)。
  • imag + other.imag4 + 2 = 6(计算新的虚部)。
  • 返回一个新的 Complex 对象,real = 4, imag = 6

4.输出结果:

  • 调用 display() 函数输出 c3 的值:
    4 + 6i
    

5.整个流程的梳理: 

6.在主函数部分:operator+ 函数是编译器自动调用的
  • 当你在代码中使用 + 运算符 作用于两个对象 时,编译器会自动去寻找并调用重载的 operator+ 函数。
  •  前提是你必须在类中提供 operator+ 函数的重载定义。
  • 出发operator的具体流程:(截图来自ChatGTP)

  • operator+ 函数的调用等价写法
    • 在表达式 c1 + c2 中,实际调用的是:
c1.operator+(c2);
  • 解释:
    • 左操作数调用 operator+,右操作数作为参数传入。计算相加结果,返回一个新对象。
    • c1 是调用 operator+ 函数的对象。
    • c2 是参数 other
    • 所以 c1 + c2 的行为与下面的代码等价:
Complex c3 = c1.operator+(c2);

 如果没有重载 + 运算符会发生什么?

如果你没有定义 operator+,编译器不知道如何处理两个 Complex 对象的 + 运算,程序会报错。如:

error: no match for ‘operator+’ (operand types are ‘Complex’ and ‘Complex’)
1.1.5补充:多个对象调用运算符重载: 
#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载 + 运算符Complex operator+(const Complex& other) {//括号内的参数表示指传入的参数是Complex型的数据并且传入参数是不可动的,是常对象,other表示这是运算符重载的右操作数return Complex(real + other.real, imag + other.imag);}void display() {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(3, 4), c2(1, 2), c3(2, 3);// 连续相加Complex c4 = c1 + c2 + c3;  // 触发两次 operator+,(c1+c2)触发一次operator,(c1+c2)的结果再加一次c3有触发一次operatorc4.display();  // 输出 6 + 9ireturn 0;
}
/*
输出:
6+9i
*/


1.2 运算符重载作为友元函数

语法格式:

friend ReturnType operator 运算符 (参数列表);
//如:
//friend Complex operator+(……)
//friend Complex operator-(……)
特点:
  • 左侧操作数可以是非当前类的对象。
  • 通常用于重载需要两个不同类型操作数的运算符(如输入输出运算符 <<>>)。

示例:重载 + 运算符(友元函数)

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 友元函数重载 +friend Complex operator+(const Complex& c1, const Complex& c2);void display() {cout << real << " + " << imag << "i" << endl;}
};// 友元函数实现
//类外补充一下:友元函数的格式 函数返回类型 友元函数名(友元函数参数)
//这里的友元函数名必须要是(operator+),表示重载+运算符,这是C++的语法规定
Complex operator+(const Complex& c1, const Complex& c2) {return Complex(c1.real + c2.real, c1.imag + c2.imag);
}int main() {Complex c1(3, 4), c2(1, 2);Complex c3 = c1 + c2;  // 调用重载的 + 运算符c3.display();return 0;
}

2. 单目运算符的重载

单目运算符(如 -, ++, --)只需要一个操作数。

成员函数形式(单目运算符)

ReturnType operator 运算符 () {// 重载逻辑
}

代码实例:1.重载 - 运算符

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载 - 运算符Complex operator-() {return Complex(-real, -imag);}void display() {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(-3, -4);   // 创建一个复数对象 c1,实部 = 3,虚部 = 4Complex c2 = -c1;   // 调用重载的 - 运算符cout << "Original Complex number: ";c1.display();cout << "Negated Complex number: ";c2.display();return 0;
}

2.代码示例:重载前置 ++-- 运算符

#include <iostream>
using namespace std;class Counter {int value;public:Counter(int v = 0) : value(v) {}// 重载前置 ++ 运算符Counter& operator++() {++value;  // 先增加return *this;  // 返回自身引用}// 重载前置 -- 运算符Counter& operator--() {--value;  // 先减少return *this;  // 返回自身引用}void display() const {cout << "Value: " << value << endl;}
};int main() {Counter c(10);++c;  // 调用前置 ++c.display();  // 输出 11--c;  // 调用前置 --c.display();  // 输出 10return 0;
}

3. 代码示例:重载后置 ++-- 运算符(后置 ++-- 需要一个 int 参数 作为区分标志。)

#include <iostream>
using namespace std;class Counter {int value;public:Counter(int v = 0) : value(v) {}// 重载后置 ++ 运算符Counter operator++(int) {//后置 ++ 和 -- 需要一个 int 参数 作为区分标志。Counter temp = *this;  // 保存当前值value++;  // 增加return temp;  // 返回之前的值}// 重载后置 -- 运算符Counter operator--(int) {//后置 ++ 和 -- 需要一个 int 参数 作为区分标志。Counter temp = *this;  // 保存当前值value--;  // 减少return temp;  // 返回之前的值}void display() const {cout << "Value: " << value << endl;}
};int main() {Counter c(10);c++;  // 调用后置 ++c.display();  // 输出 11c--;  // 调用后置 --c.display();  // 输出 10return 0;
}

 


3. 双目运算符的重载

双目运算符(如 +, -, *, ==)需要两个操作数。

成员函数形式(双目运算符)

ReturnType operator 运算符 (const Type& other) {// 重载逻辑
}

 

2.1 重载 + 运算符

代码示例:重载 + 运算符

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载 + 运算符Complex operator+(const Complex& other) const {return Complex(real + other.real, imag + other.imag);}void display() const {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(3, 4), c2(1, 2);Complex c3 = c1 + c2;  // 使用重载的 + 运算符c3.display();  // 输出 4 + 6ireturn 0;
}

2.2 重载 - 运算符

代码示例:重载 - 运算符

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载 - 运算符Complex operator-(const Complex& other) const {return Complex(real - other.real, imag - other.imag);}void display() const {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(5, 7), c2(2, 3);Complex c3 = c1 - c2;  // 使用重载的 - 运算符c3.display();  // 输出 3 + 4ireturn 0;
}

2.3 重载 * 运算符

代码示例:重载 * 运算符

#include <iostream>
using namespace std;class Multiply {int value;public://构造函数传参Multiply(int v = 1) : value(v) {}// 重载 * 运算符Multiply operator*(const Multiply& other) const {return Multiply(value * other.value);}void display() const {cout << "Value: " << value << endl;}
};int main() {Multiply m1(5), m2(6);Multiply m3 = m1 * m2;  // 使用重载的 * 运算符m3.display();  // 输出 Value: 30return 0;
}

2.4 重载 == 运算符

代码示例:重载 == 运算符

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载 == 运算符,即判断两个复数是否相等bool operator==(const Complex& other) const {return (real == other.real) && (imag == other.imag);//实部和虚部相同时这两个复数相同}void display() const {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(3, 4), c2(3, 4), c3(1, 2);if (c1 == c2) {//这里因为是两个复数对象在比较是否相等,故在这里会自动调用==重载cout << "c1 and c2 are equal" << endl;} else {cout << "c1 and c2 are not equal" << endl;}if (c1 == c3) {//同上cout << "c1 and c3 are equal" << endl;} else {cout << "c1 and c3 are not equal" << endl;}return 0;
}

4. 流插入运算符 << 和流提取运算符 >> 的重载

这两个运算符通常需要将 左操作数 定义为 ostreamistream,因此它们必须通过 友元函数 实现

语法格式:

friend ostream& operator<<(ostream& out, const Type& obj);
friend istream& operator>>(istream& in, Type& obj);

代码实例:重载 <<>> 运算符

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载 << 运算符friend ostream& operator<<(ostream& out, const Complex& c) {out << c.real << " + " << c.imag << "i";//输出一个完整的复数return out;}// 重载 >> 运算符friend istream& operator>>(istream& in, Complex& c) {cout << "输入负数的实数部分和虚数部分: ";in >> c.real >> c.imag;//输入虚数的实部和虚部return in;}
};int main() {Complex c1;cin >> c1;          // 输入重载,用于将只能输入实部和虚部的功能转化为能输入一个完整的复数输出cout << "Result: " << c1 << endl;  // 输出重载return 0;
}

关于in和out:

1.out和in是什么?

outin 其实是 大类的概念,它们属于 C++ 标准库中定义的 输入输出流体系,包含了很多 具体的输入/输出流对象

  • out:输出流对象(如 cout),用于输出数据到屏幕。
  • in:输入流对象(如 cin),用于从键盘读取输入数据。

 

2.out:输出流对象

这一行代码:

out << c.real << " + " << c.imag << "i";
  • out 是输出流对象,比如 cout(这是一个标准输出流/屏幕输出)
  • << 运算符c.real" + "c.imag 拼接成一串数据,送到输出流 out,然后显示到屏幕上。
  • 所以,这里是在将 c.realc.imag 的值以 "real + imagi" 的格式输出到屏幕。

3.in:输入流对象

这一行代码:

in >> c.real >> c.imag;
  • in 是输入流对象,比如 cin(这是一个标准输入流/键盘输入)
  • >> 运算符 从键盘输入流中读取用户输入的数据,然后赋值给 c.realc.imag
  • 这里的 c.realc.imag 属于参数 c 对象的成员变量。


5. 类型转换运算符重载

类型转换运算符重载允许自定义类的对象能够隐式或显式地转换为其他指定的类型,比如可以让自定义类对象转换为基本数据类型(如 intdouble 等),或者转换为其他用户自定义类型。这样做的好处是能够使自定义类更好地融入到 C++ 语言已有的类型体系中,方便在各种需要相应类型值的场景中使用类对象。

5.1语法格式:

operator 目标类型() const {// 转换的具体实现逻辑,返回目标类型的值return 具体要返回的值;//必须返回转换后的值
}
  • operator:表示重载的类型转换运算符。
  • 目标类型:要转换成的目标数据类型(例如 intfloat 等)。
  • const(可选):表示该函数不会修改类的成员变量。
  • 返回值:必须返回转换后的值。

5.2 特点

  1. 没有参数:类型转换运算符重载函数不能有参数。
  2. 不能指定返回类型:返回类型必须是转换目标的类型(即类型名)。
    class ErrorExample {
    public:int operator double() const {// 错误写法,不能写返回类型 intreturn 0;}
    };
  3. 可以重载多个不同类型:可以为一个类重载多个类型转换运算符。
  4. 隐式调用:类型转换运算符重载可以在需要时自动调用。

5.3 应用场景

  • 类对象 转换为 基本数据类型(如 intfloat)。
  • 类对象 转换为 另一个类的对象

C++ 运算符重载 中,强制类型转换运算符重载 是一个比较特殊的知识点。下面我会详细讲解关于强制类型转换的相关知识点,包括定义、语法、特点、应用场景和代码示例。


5.4 代码示例

示例 1:将类对象转换为 int 类型
#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 重载类型转换运算符,将 Complex 转换为 intoperator int() const {return real;  // 返回实部,作为转换后的值}void display() const {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(3, 4);// 将 c1 转换为 int 类型int intValue = c1;cout << "Complex object: ";c1.display();cout << "Converted to int: " << intValue << endl;return 0;
}
输出结果:
Complex object: 3 + 4i Converted to int: 3
解释
  • 通过重载 operator int(),当我们写 int intValue = c1; 时,编译器自动调用重载的类型转换函数。
  • 返回值为 c1 的实部 real,因此 intValue 变成了 3

示例 2:将类对象转换为 float 类型
#include <iostream>
using namespace std;class Distance {float meters;public:Distance(float m = 0) : meters(m) {}// 重载类型转换运算符,将 Distance 转换为 floatoperator float() const {return meters;  // 返回距离值}void display() const {cout << meters << " meters" << endl;}
};int main() {Distance d(5.75);// 将 d 转换为 float 类型float distanceValue = d;cout << "Distance object: ";d.display();cout << "Converted to float: " << distanceValue << endl;return 0;
}
输出结果
Distance object: 5.75 meters Converted to float: 5.75
解释
  • 重载 operator float(),实现 Distance 类到 float 类型的转换。
  • 当写 float distanceValue = d; 时,编译器自动调用该函数,返回 meters 的值。

示例 3:将类对象转换为另一个类对象
#include <iostream>
using namespace std;class Fahrenheit;class Celsius {float temperature;public:Celsius(float temp = 0) : temperature(temp) {}// 类型转换运算符:将 Celsius 转换为 Fahrenheitoperator Fahrenheit();void display() const {cout << "Celsius: " << temperature << "°C" << endl;}
};class Fahrenheit {float temperature;public:Fahrenheit(float temp = 0) : temperature(temp) {}void display() const {cout << "Fahrenheit: " << temperature << "°F" << endl;}friend class Celsius;  // 声明 Celsius 为友元类
};// 定义 Celsius 到 Fahrenheit 的类型转换
Celsius::operator Fahrenheit() {return Fahrenheit(temperature * 9 / 5 + 32);  // 摄氏度转华氏度
}int main() {Celsius c(25);  // 25°CFahrenheit f = c;  // 自动调用类型转换运算符c.display();f.display();return 0;
}
输出结果
Celsius: 25°C Fahrenheit: 77°F
解释
  • Celsius 类重载了类型转换运算符,将 Celsius 对象转换为 Fahrenheit 对象。
  • 当写 Fahrenheit f = c; 时,自动调用重载的 operator Fahrenheit(),进行摄氏度到华氏度的转换。
示例4:将自定义类对象转换为另一个自定义类类型(假设存在 Point 和 Vector 类,将 Vector 转换为 Point
#include <iostream>
using namespace std;// 点类,二维坐标表示
class Point {
private:int x;int y;public:Point(int a = 0, int b = 0) : x(a), y(b) {}void display() {cout << "(" << x << ", " << y << ")" << endl;}
};// 向量类
class Vector {
private:int dx;int dy;public:Vector(int a = 0, int b = 0) : dx(a), dy(b) {}// 重载类型转换运算符,使 Vector 类能转换为 Point 类operator Point() const {return Point(dx, dy);}
};int main() {Vector vec(3, 4);// 隐式调用类型转换运算符,将 Vector 类对象转换为 Point 类对象Point p = vec;  p.display();return 0;
}
  • 分别定义了 Point 类用于表示二维平面上的点,有 x 和 y 坐标成员变量;以及 Vector 类表示二维向量,有 dx 和 dy 成员变量。
  • 通过在 Vector 类中重载 operator Point() 类型转换运算符,定义了将 Vector 类对象转换为 Point 类对象的规则,即使用 Vector 的 dx 和 dy 分量来构造一个 Point 对象。
  • 在 main 函数中,当把 Vector 类对象赋值给 Point 类对象时,就会自动调用重载的转换运算符完成转换。
示例5:将自定义类对象转换为指针类型(以转换为 char* 类型指针为例,模拟一个简单的字符串类转换)
#include <iostream>
#include <cstring>
using namespace std;// 简单的自定义字符串类
class MyString {
private:char* data;int len;public:MyString(const char* str = "") {len = strlen(str);data = new char[len + 1];strcpy(data, str);}~MyString() {delete[] data;}// 重载类型转换运算符,使其能转换为 char* 类型指针operator char*() const {return data;}
};int main() {MyString myStr("Hello, World!");// 隐式调用类型转换运算符,将 MyString 类对象转换为 char* 类型指针char* ptr = myStr;  cout << ptr << endl;return 0;
}
  • 定义了 MyString 类,内部通过动态分配内存来存储字符串内容,有成员变量 data(指向存储字符串的字符数组)和 len(记录字符串长度)。
  • 通过重载 operator char*() 类型转换运算符,使得 MyString 类对象可以转换为 char* 类型的指针,返回其内部存储字符串的指针 data
  • 在 main 函数里,将 MyString 类对象赋值给 char* 类型指针变量时,编译器会自动调用重载的转换运算符来进行转换,之后就能像使用普通 char* 类型字符串指针一样操作它(比如输出字符串内容)。

6.扩:左操作数

6.1关于左操作数的通俗解释左操作数 是指在运算符表达式中,运算符左侧的那个操作数。

举个简单的例子:

a + b;

在这个表达式中,运算符 + 左边的 a 就是 左操作数,右边的 b 就是 右操作数

对于运算符 +

  • 左操作数a
  • 右操作数b

6.2 运算符重载中的左操作数

  • 类成员函数 重载运算符时,左操作数必须是当前类的对象,因为成员函数是由对象调用的。
  • 友元函数 重载运算符时,左操作数可以是任意类型(类对象或其他类型),因为友元函数不属于类,可以自由指定操作数。

6.3 代码实例:成员函数与友元函数的左操作数

6.3.1 类成员函数中的左操作数

在类成员函数中重载运算符时,左操作数是调用运算符的对象(当前对象 this):

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 成员函数重载 +Complex operator+(const Complex& other) {cout << "Left operand: " << real << " + " << imag << "i" << endl;return Complex(real + other.real, imag + other.imag);}void display() {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(1, 2), c2(3, 4);Complex c3 = c1 + c2;  // c1 是左操作数,c2 是右操作数c3.display();return 0;
}

输出:

Left operand: 1 + 2i 4 + 6i

解释:

  • 表达式 c1 + c2 中,c1左操作数,会调用成员函数 operator+
  • other 参数是 右操作数,即 c2

6.3.2 友元函数中的左操作数

在友元函数中,左右操作数都作为参数传递,因此 左操作数可以是非类对象

#include <iostream>
using namespace std;class Complex {int real, imag;public:Complex(int r = 0, int i = 0) : real(r), imag(i) {}// 友元函数重载 +friend Complex operator+(const Complex& c1, const Complex& c2) {cout << "Left operand: " << c1.real << " + " << c1.imag << "i" << endl;return Complex(c1.real + c2.real, c1.imag + c2.imag);}void display() {cout << real << " + " << imag << "i" << endl;}
};int main() {Complex c1(1, 2), c2(3, 4);Complex c3 = c1 + c2;  // 左操作数是 c1,右操作数是 c2c3.display();return 0;
}

输出:

Left operand: 1 + 2i 4 + 6i

解释:

  • 表达式 c1 + c2 中,c1 是左操作数,c2 是右操作数。
  • 在友元函数中,左右操作数通过参数列表传入,不需要依赖 this 指针。

6.4 左操作数与运算符重载的联系

类成员函数:
  • 左操作数是调用运算符的对象(通过 this 指针隐式传入)。
  • 示例c1 + c2 → 左操作数是 c1,调用 c1.operator+()
友元函数:
  • 左操作数和右操作数都通过参数列表传递。
  • 示例c1 + c2 → 左操作数是 c1,友元函数参数为 (c1, c2)

6.5 为什么左操作数对运算符重载很重要?

  • 类成员函数 的运算符重载要求左操作数是当前类的对象。
  • 友元函数 则没有这样的限制,提供了更大的灵活性,适用于左操作数不是类对象的情况。

六、运算符重载的总结

运算符类型重载形式使用场景
单目运算符成员函数负号 -, 递增 ++
双目运算符成员或友元函数+, 比较 ==, 赋值 =
输入输出运算符友元函数流插入 <<, 流提取 >>
类型转换运算符成员函数类对象与内置类型间的转换

七、 关键点归纳

  1. 成员函数友元函数 是运算符重载的两种实现方式。
    • 成员函数:左侧操作数必须是当前类对象。
    • 友元函数:适用于左右操作数不同的情况。
  2. 流插入运算符 << 和流提取运算符 >> 必须用友元函数重载。
  3. 单目和双目运算符 的重载遵循语法规范,不能改变操作数的数量。
  4. 类型转换运算符 通过 operator type() 重载实现对象与内置类型的转换。

相关文章:

[C++]运算符重载

一、 什么是运算符重载&#xff1f; 运算符重载是 C 中的一种功能&#xff0c;它允许用户定义的类或数据类型重新定义或扩展运算符的行为&#xff0c;使运算符能够作用于用户定义的对象。 二、 通俗解释 在 C 中&#xff0c;运算符&#xff08;如 , -, *, 等&#xff09;默认…...

C++基础

01引用的本质 int a 10;/**引用本质是指针常量&#xff0c;指针指向不可更改,因此引用一旦初始化就不可以更改*自动转换为int* const ref&a;*/int &ref a;/*内部发现是引用&#xff0c;自动转换为*ref20;*/ref20; 02函数高级 2.1默认参数 #include <iostream&g…...

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现

鸿蒙项目云捐助第七讲鸿蒙App应用的首页推荐模块布局的实现 最后设置首页的推荐模块&#xff0c;参考模板如下图所示。 一、首页热门推荐模块的实现 对于热门推荐模块&#xff0c;先有上面的小标题栏&#xff0c;这里的标题栏也有一个小图标&#xff0c;首先从“百度图库”中…...

【网络安全设备系列】1、防火墙

0x00 前言 最近由于工作原因&#xff0c;需要详细如今各类网络安全设备&#xff0c;所以开了此系列文章&#xff0c;希望通过对每个网络安全设备进行整理总结&#xff0c;来详细了解各类网络安全设备作用功能以及实现原理、部署配置方法等。 0x01 定义&#xff1a;防火墙指的…...

C# 备份文件夹

C# 备份目标文件夹 方法1&#xff1a;通过 递归 或者 迭代 结合 C# 方法 参数说明&#xff1a; sourceFolder&#xff1a;源文件夹路径destinationFolder&#xff1a;目标路径excludeNames&#xff1a;源文件夹中不需备份的文件或文件夹路径哈希表errorLog&#xff1a;输出错…...

【sizeof】各种数据类型所占空间大小

各种数据类型所占空间大小 文章目录 前言 一、sizeof是什么&#xff1f; 二、使用步骤 1.整型 2.字符型 总结 前言 ‌sizeof在C语言中是一个运算符&#xff0c;用于获取数据类型或变量在内存中所占的字节数。‌它可以在编译时计算数据类型或变量的内存大小&#xff0c;而…...

水仙花数(流程图,NS流程图)

题目&#xff1a;打印出所有的100-999之间的"水仙花数"&#xff0c;并画出流程图和NS流程图。所谓"水仙花数"是指一个三位数&#xff0c;其各位数字立方和等于该数本身。例如&#xff1a;153是一个"水仙花数"&#xff0c;因为1531的三次方&#…...

wireshark捕获过滤和操作方式详解

大家觉得有用记得关注和点赞&#xff0c;谢谢。 一、Wireshark介绍 Wireshark&#xff08;前身是Ethereal&#xff09;是一个网络封包分析软件&#xff0c;目前是全球使用最广泛的开源抓包软件&#xff0c;别名小鲨鱼或者鲨鱼鳍。 网络封包分析软件的功能是截取网卡进出的网络…...

ChatGPT Search开放:实时多模态搜索新体验

点击访问 chatTools 免费体验GPT最新模型&#xff0c;包括o1推理模型、GPT4o、Claude、Gemini等模型&#xff01; ChatGPT Search&#xff1a;功能亮点解析 本次更新的ChatGPT Search带来了多项令人瞩目的功能&#xff0c;使其在搜索引擎市场中更具竞争力。 1. 高级语音模式&…...

【docker】docker swarm常用命令以及电商平台构建案例

1. 初始化Swarm集群 用于初始化一个Swarm集群&#xff0c;并将当前节点设置为Manager节点。 docker swarm init 用法&#xff1a; docker swarm init --advertise-addr <Manager节点IP>示例&#xff1a; docker swarm init --advertise-addr 192.168.1.100这会将当前节…...

3D计算机视觉概述

3D计算机视觉 3D计算机视觉概述 像机标定 文章目录 3D计算机视觉前言一、人类视觉二、计算机视觉2.1 计算机视觉的研究目的2.2 计算机视觉的研究任务2.3 计算机视觉的研究方法2.4 视觉计算理论2.5 马尔框架中计算机视觉表达的四个层次2.5.1 图像&#xff08;像素表达&#xff…...

23. 合并 K 个升序链表(java)

题目描述&#xff1a; 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#xff1a;[1,1,2,3,4,4,5,6] 解释&#xff…...

与乐鑫相约 CES 2025|创新技术引领物联网与嵌入式未来

2025 国际消费电子产品展览会 (International Consumer Electronics Show, CES) 将于 2025 年 1 月 7 至 10 日在美国拉斯维加斯盛大开幕。作为全球规模最大、水准最高&#xff0c;且影响力最广的消费电子类科技盛会&#xff0c;CES 每年都吸引着全球行业领袖、开发者和技术爱好…...

MIPS指令集(一)基本操作

目录 计算机硬件的操作数 存储器操作数 常数或立即数操作数 有符号数和无符号数 指令的格式 逻辑操作 决策指令 循环 计算机硬件的操作数 先从一条C语句入手 a b c; 将其翻译为MIPS add a, b, c 其中a&#xff0c;b&#xff0c;c就是这条指令的操作数。表示将b与c…...

半导体数据分析(二):徒手玩转STDF格式文件 -- 码农切入半导体系列

一、概述 在上一篇文章中&#xff0c;我们一起学习了STDF格式的文件&#xff0c;知道了这是半导体测试数据的标准格式文件。也解释了为什么码农掌握了STDF文件之后&#xff0c;好比掌握了切入半导体行业的金钥匙。 从今天开始&#xff0c;我们一起来一步步地学习如何解构、熟…...

在window环境下安装openssl生成钥私、证书和签名,nodejs利用express实现ssl的https访问和测试

在生成我们自己的 SSL 证书之前&#xff0c;让我们创建一个简单的 Express应用程序。 要创建一个新的 Express 项目&#xff0c;让我们创建一个名为node-ssl -server 的目录&#xff0c;用终端cmd中进入node-ssl-server目录。 cd node-ssl-server 然后初始化一个新的 npm 项目…...

C++中类和对象的细节原理

文章目录 一、C中的构造函数二、C中的析构函数三、两者的配合与注意事项四、C中的静态成员变量五、C中的静态成员函数六、C中普通成员函数和静态成员函数的区别七、C中的const成员变量八、C中的const 成员函数九、C中构造函数的初始化列表十、C中的浅拷贝操作十一、C中的深拷贝…...

解决并发情况下调用 Instruct-pix2pix 模型推理错误:index out of bounds 问题

解决并发情况下调用 Instruct-pix2pix 模型推理错误&#xff1a;index out of bounds 问题 背景介绍 在对 golang 开发的 图像生成网站 进行并发测试时&#xff0c;调用基于 Instruct-pix2pix 模型和 FastAPI 的图像生成 API 遇到了以下错误&#xff1a; Model inference er…...

OpenXLSX开源库在 Ubuntu 18.04 的编译、交叉编译与使用教程

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…...

windows和LINUX下校验文件的哈希值(MD5、SHA256)

可以通过两个文件的哈希值来对比两个文件是不是一模一样&#xff0c;有没有缺失 1、windows CertUtil -hashfile 文件路径 MD5 CertUtil -hashfile 文件路径 SHA256 2、Liunx 校验当前目录下所有文件 sha256sum . 校验指定文件名 sha256sum 文件名...

〔 MySQL 〕视图

以下是上述文章的目录&#xff1a; 一、视图概述 视图的定义 二、基本使用 创建视图查询视图修改视图影响基表查询验证删除视图 三、视图规则和限制 命名规则数量限制索引和触发器安全性ORDER BY规则与表一起使用 四、实战案例 牛客实战OJ修改基表影响视图查询验证删除…...

嵌入式硬件产品:CC254x 蓝牙升级

目录 固件更新 OAD原理 作者简介 固件更新 支持固件更新的CC2541芯片中, 包括三段代码: Boot Image Manager(BIM)、ImageA、Im...

Drag and Drop API 实现 JavaScript 中的原生拖放功能

理解什么是拖放&#xff0c;我们先做个简单的实验。鼠标移动到页面左上角“CSDN” 图片上方&#xff0c;点击左键不放开&#xff0c;拖动鼠标&#xff0c;发现图片随着鼠标移动&#xff0c;松开鼠标时&#xff0c;图片消失。 一、拖放&#xff08;Drag and Drop&#xff09;有…...

人脸检测的若干思考!!!

1.目前主要有人脸检测方法分类&#xff1f; 主要包含两类&#xff1a;传统人脸检测算法和基于深度学习的人脸检测算法。 传统人脸检测算法主要可以分为4类&#xff1a; 基于知识、模型、特征和外观的人脸检测方法&#xff1b; 基于深度学习的方法&#xff1a;基于级联CNN的人脸…...

【时间序列分析】斯皮尔曼(Spearman)相关系数理论基础及python代码实现

文章目录 1. 斯皮尔曼相关系数1.1 公式定义1.2 计算过程1.3 计算示例1.4 注意事项&#xff08;当有重复值时&#xff09; 2. 优缺点2.1 优点2.2 缺点 3. 适用场景4. Python代码实现4.1 调用scipy库 5 思考5.1 什么是单调关系&#xff1f;与线性关系的区别是什么&#xff1f;5.2…...

python | linux | ModuleNotFoundError: No module named ‘WFlib‘ |找不到模块

问题&#xff1a; (base) beautyby521-7:~/Website-Fingerprinting-Library-master$ bash scripts/NetCLR.sh Traceback (most recent call last):File "/home/beauty/Website-Fingerprinting-Library-master/exp/pretrain.py", line 8, in <module>from WFli…...

B-TREE详解

B - tree 的详细结构特点 节点结构细节 关键字存储方式&#xff1a;B - tree 节点中的关键字是按照一定顺序排列的&#xff0c;这个顺序可以是升序或者降序。例如&#xff0c;在一个以数字为关键字的 B - tree 中&#xff0c;关键字从左到右依次增大。每个关键字都有一个分隔作…...

Kotlin复习

一、Kotlin类型 1.整数 2.浮点 显示转换&#xff1a; 所有数字类型都支持转换为其他类型&#xff0c;但是转换前会检测长度。 toByte(): Byte toShort(): Short toInt(): Int toLong(): Long toFloat(): Float toDouble(): Double 不同进制的数字表示方法&#xff08;为了提高…...

批处理文件的创建与编辑方法

批处理命令保存在以BAT为扩展名地文本文件中&#xff0c;因此可以使用任何字处理软件创建、编辑批处理文件&#xff0c;如Word、WinHex、Editpuls等。 案例一 使用copy con命令创建批处理文件 ”copy con“是一个功能简单、易于使用的创建文本文件命令&#xff0c;命令中”con…...

Spring Boot集成Kafka:最佳实践与详细指南

文章目录 一、生产者1.引入库2.配置文件3.配置类PublicConfig.javaMessageProducer.java 4.业务处理类 三、消费者1.引入库2.配置类PublicConfig.javaMessageConsumer.java 3.业务类 一、生产者 1.引入库 引入需要依赖的jar包&#xff0c;引入POM文件&#xff1a; <depend…...

maven 中 有历史模块缓存 怎么清

Maven 在运行时会将一些数据保存在本地仓库中&#xff0c;以加快构建过程。其中一部分是项目的依赖项&#xff0c;还有就是“历史模块缓存”。这些缓存信息保存在本地仓库的 _remote.repositories 文件中。 解决方案&#xff1a; 手动删除缓存文件&#xff1a; 进入你的Maven本…...

云计算HCIP-OpenStack04

书接上回&#xff1a; 云计算HCIP-OpenStack03-CSDN博客 12.Nova计算管理 Nova作为OpenStack的核心服务&#xff0c;最重要的功能就是提供对于计算资源的管理。 计算资源的管理就包含了已封装的资源和未封装的资源。已封装的资源就包含了虚拟机、容器。未封装的资源就是物理机提…...

【信息系统项目管理师-论文真题】2015下半年论文详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论题一:大项目或多项目的成本管理解题思路写作要点论题二:项目的采购管理解题思路写作要点论题一:大项目或多项目的成本管理 随着移动互联网、物联网、云计算、大数据等新一代信息技术的广泛应用,我国目前…...

C# 面试中常见递归算法

前言 今天我们主要总结一下C#面试中常见递归算法。 C#经典十大排序算法&#xff08;完结&#xff09; C#递归算法计算阶乘的方法 一个正整数的阶乘&#xff08;factorial&#xff09;是所有小于及等于该数的正整数的积&#xff0c;并且0的阶乘为1。自然数n的阶乘写作n!。180…...

qemu源码解析【02】qom基本概念

目录 qemu源码解析【02】qom基本概念参考资料基本数据结构TypeImplObjectClassObjectTypeInfo qemu源码解析【02】qom基本概念 参考资料 https://blog.csdn.net/u011364612/article/details/53485856qemu中为了模拟各种虚拟设备和总线&#xff0c;采用了面向对象的思想&#…...

C++算法第八天

本篇文章我们继续学习c算法 目录 第一题 题目链接 题目展示 代码原理 代码编写 第二题 题目链接 题目展示 代码原理 代码编写 第三题 题目链接 题目展示 代码原理 代码编写 第一题 题目链接 69. x 的平方根 - 力扣&#xff08;LeetCode&#xff09; 题目展示…...

大语言模型的常用微调方法

文章目录 数据构造与清洗数据构造方法1.基于 Self-Instruct 方法的数据构造2.面向结构化知识的数据构造 数据清洗方法1.IFD 指标法2. MoDS 方法 2.2 分词器构造2.2.1 分词器概述BPE 分词器WordPiece 分词器Unigram 分词器 2.3 大型语言模型的微调方法前缀调优提示调优P-Tuning …...

安卓获取所有可用摄像头并指定预览

在Android设备中&#xff0c;做预览拍照的需求的时候&#xff0c;我们会指定 CameraSelector DEFAULT_FRONT_CAMERA前置 或者后置CameraSelector DEFAULT_BACK_CAMERA 如果你使用的是平板或者工业平板&#xff0c;那么就会遇到多摄像头以及外置摄像头问题&#xff0c;简单的指…...

Jwt和Session的区别和优缺点

目录 一、Authentication和Authorization的区别 二、用户认证的两种主流方法 ① 、基于session认证流程如下&#xff1a; ②、基于token&#xff08;令牌&#xff09;用户认证如下&#xff1a; 三、区别和优缺点 JWT的优点&#xff1a; JWT的缺点&#xff1a; 安全性 …...

如何使用 Python 读取和写入 CSV 文件?

在Python中&#xff0c;处理CSV文件是一项常见的任务&#xff0c;通常用于数据交换和数据存储。 Python的标准库csv模块提供了一种方便的方式来读取和写入CSV文件。 下面我将详细介绍如何使用Python的csv模块来读取和写入CSV文件&#xff0c;并提供一些实际开发中的建议和注意…...

FR2012富芮坤PMU:设置PMU GPIO为输入读取状态;在睡眠功能中读取IO;PMU GPIO设置唤醒源中断

从事嵌入式单片机的工作算是符合我个人兴趣爱好的,当面对一个新的芯片我即想把芯片尽快搞懂完成项目赚钱,也想着能够把自己遇到的坑和注意事项记录下来,即方便自己后面查阅也可以分享给大家,这是一种冲动,但是这个或许并不是原厂希望的,尽管这样有可能会牺牲一些时间也有哪天原…...

私有云dbPaaS为何被Gartner技术成熟度曲线标记为“废弃”?

当云计算席卷而来&#xff0c;基于云基础设施的数据库部署也改变了数据库。在传统的私有化部署&#xff08;On-premises&#xff09;和公有云部署&#xff08;Public Cloud&#xff09;之间&#xff0c;不断融合的混合IT&#xff08;Mixed IT&#xff09;形式成为最常见的企业级…...

RabbitMQ 高级特性——延迟队列

文章目录 前言延迟队列延迟队列的概念TTL 死信队列模拟延迟队列设置队列的 TTL设置消息的 TTL 延迟队列插件安装并且启动插件服务使用插件实现延迟功能 前言 前面我们学习了 TTL 和死信队列&#xff0c;当队列中的消息达到了过期时间之后&#xff0c;那么这个消息就会被死信交…...

Perl 引用

Perl 引用 Perl&#xff0c;作为一种灵活而强大的编程语言&#xff0c;广泛用于系统管理、网络编程、GUI开发等领域。在Perl编程中&#xff0c;引用&#xff08;References&#xff09;是一个核心概念&#xff0c;它允许变量引用其他数据&#xff0c;从而创建复杂的数据结构&a…...

llama2——微调lora,第一次参考教程实践完成包括训练和模型

前言&#xff1a;磕磕绊绊&#xff0c;不过收获很多&#xff0c;最大的收获就是解决报错error的分析方法和解决思路 1、首先&#xff0c;我参考的是这篇博客&#xff1a;怎样训练一个自己的大语言模型&#xff1f;全网最简单易懂的教程&#xff01;_开源模型训练出一个语言模型…...

【人工智能数学基础篇】——深入详解矩阵分解:奇异值分解(SVD)与主成分分析(PCA)在数据降维与特征提取中的应用

目录 1. 引言 2. 矩阵分解概述 2.1 矩阵分解的意义 3. 奇异值分解&#xff08;SVD&#xff09; 3.1 定义与数学基础 3.2 SVD 的性质 3.3 SVD 在数据降维中的应用 3.4 示例代码&#xff1a;使用 SVD 进行图像压缩 3.5 结果分析 4. 主成分分析&#xff08;PCA&#xff0…...

C++常见面试题-初级2

1. C和C有什么区别&#xff1f; C是面向对象的语言&#xff0c;而C是面向过程的语言&#xff1b;C引入new/delete运算符&#xff0c;取代了C中的malloc/free库函数&#xff1b;C引入引用的概念&#xff0c;而C中没有&#xff1b;C引入类的概念&#xff0c;而C中没有&#xff1…...

R square 的计算方法和一点思考

模型的性能评价指标有几种方案&#xff1a;RMSE&#xff08;平方根误差&#xff09;、MAE&#xff08;平均绝对误差&#xff09;、MSE(平均平方误差)、R2_score 其中&#xff0c;当量纲不同时&#xff0c;RMSE、MAE、MSE难以衡量模型效果好坏。这就需要用到R2_score&#xff1…...

梳理你的思路(从OOP到架构设计)_简介EIT造形

目录 1、 复习<基类 /子类>的扩充(extends)结构 典型的<基类/子类>代码结构 <基类/子类>代码结构的变形 2、 从<基类/子类>结构到EIT造形 3、 EIT造形的基本形与变形 1、 复习<基类 /子类>的扩充(extends)结构 典型的<基类/子类>代码…...

梳理你的思路(从OOP到架构设计)_认识EIT造形与内涵

目录 1、 认识类(Class)与内涵 2、 认识EIT造形与内涵 EIT造形&#xff1a; 类造形的组合​编辑 复习EIT的基本形与变形​编辑 不同内涵 EIT造形 1、 认识类(Class)与内涵 回顾 类(Class)是比较小的造形 各种不同内涵&#xff0c;可以透过类(Class)的形式(Form)来呈现出…...