C++ 运算符重载全解析:+、=、>>、<<、++、==

什么是运算符重载?

        运算符重载是 C++ 提供的一种语法扩展机制,使得自定义类也可以使用类似于内置类型的操作符,换句话说,你可以为你的类赋予自然的操作符语义,让代码更优雅、更贴近业务逻辑。 

基本语法

        重载函数本质上是一个特殊函数:

返回类型 operator符号(参数列表) 

它可以是:

  • 成员函数
  • 非成员函数(全局函数,或友元) 

一、重载 + 运算符 

为什么要重载 + 运算符?

        假设你有一个二维向量类:

class Vector2D { public: int x, y; }; 

        现在你希望支持类似以下操作:

Vector2D a(1, 2), b(3, 4); Vector2D c = a + b; // 理想行为 

        这是不可能的,除非你 告诉编译器“+”在你的类中应该怎么用 —— 也就是重载它。 

如何重载 + 运算符?

成员函数版本

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

使用方式:

Vector2D a(1, 2), b(3, 4); Vector2D c = a + b; // 自动调用 operator+ 
建议使用 const 引用作为参数,避免不必要的拷贝
const 成员函数表示不会修改当前对象 

全局函数版本(推荐)

        有些时候你希望支持这种写法:

Vector2D a(1, 2); Vector2D c = a + 5; // 或者 5 + a; 

        此时就不能用成员函数,因为左侧的 5 并不是类对象。

做法:写成友元 + 全局函数

class Vector2D { public: int x, y; Vector2D(int x = 0, int y = 0) : x(x), y(y) {} friend Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs); }; Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs) { return Vector2D(lhs.x + rhs.x, lhs.y + rhs.y); } 

支持混合类型加法

        你还可以重载 int + Vector2D 或 Vector2D + int:

Vector2D operator+(const Vector2D& v, int val) { return Vector2D(v.x + val, v.y + val); } Vector2D operator+(int val, const Vector2D& v) { return Vector2D(v.x + val, v.y + val); } 

小练习:重载 + 支持字符串拼接类

class MyString { string data; public: MyString(string s) : data(s) {} MyString operator+(const MyString& other) const { return MyString(data + other.data); } friend ostream& operator<<(ostream& os, const MyString& s) { os << s.data; return os; } }; 

使用:

MyString a("Hello, "), b("World!"); cout << a + b << endl; // 输出:Hello, World! 

二、重载 += 运算符 

 operator+=operator+ 的区别

运算符是否修改自身返回值类型性能
+❌ 不修改新对象中等
+=✅ 修改自身返回引用(*this更优

        举例对比:

a = a + b; // 创建新对象并赋值 a += b; // 原地修改 a,更高效 

基本写法(成员函数)

class Vector2D { public: int x, y; Vector2D(int x = 0, int y = 0) : x(x), y(y) {} Vector2D& operator+=(const Vector2D& other) { x += other.x; y += other.y; return *this; } }; 

使用:

Vector2D a(1, 2), b(3, 4); a += b; // a = (4, 6) 

返回 *this 的意义?

        返回引用是为了支持链式调用:

a += b += c; 

        这样 b += c 会返回 b 的引用,接着 a += (b),逻辑就通了。 

建议搭配 operator+ 一起写

        通常这样配套使用:

Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs) { Vector2D temp = lhs; temp += rhs; return temp; } 

        这样避免了重复逻辑:+ 内部直接调用 +=,更清晰。 

示例:重载 += 实现字符串拼接类

class MyString { string data; public: MyString(string) : data(s) {} MyString& operator+=(const MyString& other) { data += other.data; return *this; } friend ostream& operator<<(ostream& os, const MyString& s) { os << s.data; return os; } }; 

使用:

MyString a("Hello "), b("World"); a += b; cout << a; // 输出 Hello World 

三、重载 >> 实现自定义输入

        默认情况下,cin 只能读取内置类型(int、float、string 等)。对于自定义类,你必须自己定义输入逻辑。

        通过重载 operator>>,你可以实现如下优雅写法:

cin >> person; // 自动读取 name、age、gender 等字段 

 基本语法(推荐写成全局友元函数)

class Person { public: string name; int age; // 声明为友元函数,允许访问私有成员 friend istream& operator>>(istream& in, Person& p); }; // 实现 istream& operator>>(istream& in, Person& p) { in >> p.name >> p.age; return in; } 

使用示例:

Person p; cin >> p; 

        注意:引用传参 + 返回流对象引用 是标准做法,支持链式输入。 

完整示例:输入二维坐标点

class Point { public: int x, y; friend istream& operator>>(istream& in, Point& p); friend ostream& operator<<(ostream& out, const Point& p); }; istream& operator>>(istream& in, Point& p) { cout << "请输入x和y:"; in >> p.x >> p.y; return in; } ostream& operator<<(ostream& out, const Point& p) { out << "(" << p.x << ", " << p.y << ")"; return out; } 

使用方式:

Point pt; cin >> pt; cout << pt; // 输出 (x, y) 

 应用场景举例

  1. 输入学生信息
cin >> student; // 读取姓名、学号、成绩等 
  1. 输入日期
cin >> date; // 读取年、月、日 
  1. 批量数据输入
vector<Point> vec(3); for (auto& pt : vec) cin >> pt; 

小技巧:配合 getline

        如果类成员中有空格,如字符串:

class Book { public: string title; int pages; friend istream& operator>>(istream& in, Book& b) { getline(in >> ws, b.title); // 跳过前导空格 in >> b.pages; return in; } }; 

四、重载 << 实现自定义输出 

        默认情况下,cout << 只能用于内置类型和部分 STL 类型(如 stringvector<int> 等)。

对于自定义类,如 PersonDatePoint,你需要自己定义如何将对象内容格式化输出到流中。

        重载 << 后的代码将更直观、可维护性更高:

cout << person << endl; 

 基本语法:推荐使用全局友元函数

class Person { public: string name; int age; // 友元声明,允许访问私有成员 friend ostream& operator<<(ostream& out, const Person& p); }; // 函数定义 ostream& operator<<(ostream& out, const Person& p) { out << "姓名: " << p.name << ", 年龄: " << p.age; return out; } 

注意:

  • 参数用 常引用,避免拷贝
  • 返回 ostream&,支持链式输出 

完整示例:输出二维坐标点

class Point { private: int x, y; public: Point(int x = 0, int y = 0) : x(x), y(y) {} friend ostream& operator<<(ostream& out, const Point& p); }; ostream& operator<<(ostream& out, const Point& p) { out << "(" << p.x << ", " << p.y << ")"; return out; } 

使用方式:

Point pt(3, 4); cout << pt << endl; // 输出 (3, 4) 

支持链式调用

        由于 cout 是一个 ostream 类型对象,重载函数返回 ostream& 可支持多次连续输出:

cout << p1 << ", " << p2 << ", " << p3 << endl; 

        如果你忘记 return out;,将会破坏链式调用行为。 

常见错误

错误写法正确写法
void operator<<(ostream&, T&)ostream& operator<<(ostream&, const T&)
写成类的成员函数(不推荐)写成友元的全局函数
忘记返回 out应该 return out; 支持链式调用

实际项目应用举例

  1. 打印调试对象
cout << user; // 打印用户信息 
  1. 格式化输出日志信息
logFile << "[INFO] " << timestamp << ": " << event << endl; 
  1. 配合泛型容器使用
vector<Person> vec = {p1, p2}; for (auto& p : vec) cout << p << endl; 

配合文件流也适用

        重载的 << 不仅支持 cout,也支持其他 ostream 类型如 ofstream

ofstream out("person.txt"); out << p1 << endl; 

重载<<与>>的区别 

运算符目的传参类型返回类型
operator<<输出到流ostream&, const T&ostream&
operator>>从流中读取istream&, T&istream&

五、重载自增运算符 ++(前置与后置) 

基础回顾:什么是 ++

        自增运算符 ++ 是一种一元运算符,可以作用于变量前或后:

int a = 5; ++a; // 前置 ++a:先加后用 a++; // 后置 a++:先用后加 

        同样地,我们可以将这种语义赋予自己的类,比如:

MyInteger a(10); ++a; // 让a的内部值+1 a++; // 后置自增 

前置和后置 ++ 的底层区别

        在 函数签名层面,C++ 是通过参数来区分前置和后置的:

类型函数原型说明
前置 ++aMyClass& operator++()无参数,返回引用
后置 a++MyClass operator++(int)有一个 int 虚参,返回值拷贝

        注:int 参数只是为了区分用的,不会传递值进去。 

完整示例代码

        让我们通过一个例子来看如何重载前置与后置 ++

#include <iostream> using namespace std; class MyInteger { private: int val; public: MyInteger(int v = 0) : val(v) {} // 前置 ++a MyInteger& operator++() { ++val; // 先加 return *this; // 返回自身引用 } // 后置 a++ MyInteger operator++(int) { MyInteger temp = *this; // 保存旧值 val++; // 自增 return temp; // 返回旧值 } void show() const { cout << "val = " << val << endl; } }; 

使用示例:

int main() { MyInteger a(10); ++a; // 前置:a变11 a.show(); a++; // 后置:a仍然变12,但返回旧值 a.show(); MyInteger b = ++a; // b = 13, a = 13 b.show(); MyInteger c = a++; // c = 13, a = 14 c.show(); } 

返回值差异详解

类型返回类型原因
前置 ++aT&(引用)可以链式调用,避免拷贝
后置 a++T(值拷贝)要返回旧值,所以需要保留临时对象
++(++a); // 合法:前置返回引用 (a++)++; // 不合法:后置返回临时变量,不能再自增 

链式调用场景

        我们可以支持链式写法:

MyInteger a(1); (++a)++; a.show(); // 合法:先++a,得到引用,再执行后置++ 

这就要求:

  • 前置返回引用 T&
  • 后置返回临时值 T 

常见错误

错误写法正确写法
没写后置函数参数 int必须写一个虚拟 int 参数区分
后置返回引用 T&应该返回 T 的拷贝
前置返回 voidT应返回引用 T& 以支持链式调用

面试经典问题

💬 “C++ 中如何实现 a++ 与 ++a 的区别?”

答:通过重载不同的函数原型实现:

  • T& operator++() 实现前置
  • T operator++(int) 实现后置,其中 int 是虚参,仅用于区分

并且返回类型设计为:前置返回引用、后置返回副本。 

六、重载 == 来比较对象是否相等

为什么我们要重载 ==

        在 C++ 中,内置类型之间可以直接比较:

int a = 10, b = 20; cout << (a == b); // 输出 0(false) 

        但你自己写的类对象,默认不能比较“值相等”:

class Person { public: string name; int age; }; int main() { Person p1{"Tom", 18}; Person p2{"Tom", 18}; cout << (p1 == p2); // ❌ 报错!没有定义 == 运算符 } 

        原因:C++ 不知道怎么“比较两个对象”,默认只会比较地址或不支持。因此你需要自己告诉它什么是“两个对象相等”。 

如何重载 == 运算符?

        你可以在类中写一个特殊的函数,名字就叫 operator==,告诉 C++ 如何比较两个对象。

基本语法:

bool operator==(const ClassName& other) const; 

完整案例讲解

        我们来写一个完整类,表示“人”的信息:姓名和年龄。我们希望两个 Person 如果名字和年龄都一样,就认为他们是同一个人。

#include <iostream> using namespace std; class Person { public: string name; int age; // 构造函数 Person(string n, int a) : name(n), age(a) {} // 运算符重载:判断两个对象是否相等 bool operator==(const Person& other) const { return this->name == other.name && this->age == other.age; } }; 

测试代码:

int main() { Person p1("Tom", 18); Person p2("Tom", 18); Person p3("Jerry", 20); if (p1 == p2) { cout << "p1 和 p2 相等!" << endl; } else { cout << "p1 和 p2 不相等!" << endl; } if (p1 == p3) { cout << "p1 和 p3 相等!" << endl; } else { cout << "p1 和 p3 不相等!" << endl; } } 

输出结果:

p1 和 p2 相等! p1 和 p3 不相等! 

重点解释每一行代码

bool operator==(const Person& other) const 
关键词解释
bool返回值是布尔类型,true 表示相等,false 表示不相等
operator==告诉 C++ 这是一个运算符函数,重载 == 运算符
const Person& other比较的另一个对象,引用可以避免拷贝,const防止被修改
const(末尾)表示这个函数不会修改当前对象,更安全,推荐写

== 重载必须写在类里面吗?

        不一定。你也可以写在类外,但推荐写在类里,逻辑更清晰。

        类外写法(不推荐给新手):

bool operator==(const Person& a, const Person& b) { return a.name == b.name && a.age == b.age; } 

注意事项总结

常见问题正确做法
忘记加 const 引用比较函数参数要用 const ClassName&,避免拷贝提高性能
没有定义 == 就直接比较对象会报错,需要重载
比较逻辑不清晰根据你对象的数据来决定比较什么字段

配合重载 !=

        通常 ==!= 是成对出现的,只要写好 ==,你可以轻松写:

bool operator!=(const Person& other) const { return !(*this == other); // 复用前面的 == } 

== 运算符重载和 STL 一起用!

你定义好 == 后,你就可以使用:

  • std::find 查找元素
  • std::set, std::map 比较键值
  • 判断对象是否存在于容器中
#include <vector> #include <algorithm> vector<Person> vec = {p1, p2}; if (find(vec.begin(), vec.end(), p1) != vec.end()) { cout << "找到了!" << endl; } 

七、 重载 [](数组访问运算符) 

为什么要重载 []

        我们知道,C++内置数组可以通过下标访问元素:

int arr[5] = {10, 20, 30, 40, 50}; cout << arr[2]; // 输出 30 

        但是,如果你写了自己的类,想用 obj[2] 这种写法访问类里的数据,C++默认是不支持的,因为编译器不知道你这个类应该怎么“用下标取数据”。

        这时候,我们就需要重载 [] 运算符,告诉编译器“你该怎么用下标访问我的类”。

[] 运算符重载的基本语法

返回类型& operator[](参数); 
  • 返回类型&:一般返回的是引用,方便修改数据(可读可写)
  • operator[]:告诉编译器这是重载的[]运算符
  • 参数:下标,一般用 int 表示访问哪个元素

 举个最简单的例子

#include <iostream> using namespace std; class MyArray { private: int data[5]; public: MyArray() { for (int i = 0; i < 5; ++i) data[i] = i * 10; } int& operator[](int index) { return data[index]; // 返回对应位置元素的引用 } }; int main() { MyArray arr; cout << arr[2] << endl; // 输出 20 arr[2] = 99; // 修改数据 cout << arr[2] << endl; // 输出 99 return 0; } 

重点讲解

  • 为什么返回引用 int&
    因为返回引用,可以既读又写。比如上面例子,arr[2] = 99; 修改了数组元素。如果返回的是值,就不能修改原数据。
  • 参数 index 是什么?
    你用下标访问时,传入的就是这个数字,比如 arr[2]index 就是2。

增加“安全措施”——越界检查

        原生数组不会帮你检查下标是否合法,访问越界会导致错误。我们可以加代码手动检查:

int& operator[](int index) { if (index < 0 || index >= 5) { cout << "⚠️ 越界访问,返回第一个元素!" << endl; return data[0]; } return data[index]; } 

支持 const 对象访问

        如果对象是 const,它调用的是 const 版本的 operator[],这时候我们要写一个只读版本:

int operator[](int index) const { if (index < 0 || index >= 5) { cout << "⚠️ 越界访问,返回0!" << endl; return 0; } return data[index]; } 

const 版本返回的是值,不能修改数据,只能读。

完整示例代码(含注释)

#include <iostream> using namespace std; class MyArray { private: int data[5]; public: MyArray() { for (int i = 0; i < 5; ++i) data[i] = i * 10; } // 非const版本,返回引用,可读写 int& operator[](int index) { if (index < 0 || index >= 5) { cout << "⚠️ 越界访问,返回第一个元素!" << endl; return data[0]; } return data[index]; } // const版本,只读访问 int operator[](int index) const { if (index < 0 || index >= 5) { cout << "⚠️ 越界访问,返回0!" << endl; return 0; } return data[index]; } }; int main() { MyArray arr; cout << arr[2] << endl; // 20 arr[2] = 99; cout << arr[2] << endl; // 99 const MyArray cArr; cout << cArr[3] << endl; // 30 // cArr[3] = 50; // ❌ 不能修改,编译报错 cout << arr[10] << endl; // 越界访问提示 } 

 八、重载赋值运算符= 

        赋值运算符=是我们日常最常用的操作之一,比如:

int a = 5; int b = 10; b = a; // 把a的值赋给b,b变成5 

        在C++里,赋值操作是用=来完成的。

为什么需要重载赋值运算符?

        当你定义自己的类时,比如:

class Person { public: string name; int age; }; 

        如果写了:

Person p1, p2; p2 = p1; 

编译器默认会给你生成一个赋值运算符,但它只做简单的“逐成员赋值”(浅拷贝)。

        如果你的类里有指针或动态分配的资源(比如堆内存),简单的浅拷贝会导致两个对象指向同一块内存,出问题!

        所以,我们要自己重载赋值运算符,保证赋值时数据被正确复制(深拷贝),避免程序崩溃和内存错误。

赋值运算符重载的格式

ClassName& operator=(const ClassName& rhs); 
  • ClassName&:返回自身对象的引用,支持链式赋值(a = b = c;
  • operator=:表示重载赋值运算符
  • const ClassName& rhs:右侧赋值对象的常量引用,避免拷贝,提高效率
#include <iostream> #include <string> using namespace std; class Person { public: string name; int age; // 赋值运算符重载函数 Person& operator=(const Person& rhs) { // 1. 自我赋值检测,防止 p = p 时出错 if (this == &rhs) return *this; // 2. 逐成员赋值,将 rhs 的数据复制给当前对象 name = rhs.name; age = rhs.age; // 3. 返回自身引用,支持链式赋值 return *this; } }; int main() { Person p1; p1.name = "Alice"; p1.age = 30; Person p2; p2 = p1; // 调用赋值运算符重载 cout << "p2.name = " << p2.name << ", p2.age = " << p2.age << endl; } 
  • Person& operator=(const Person& rhs)
    定义赋值运算符函数,rhs 是右侧的赋值对象,使用const &避免复制且保证不被修改。
  • if (this == &rhs)
    判断自己是否给自己赋值。如果是,就直接返回,避免重复赋值带来的问题(比如内存释放两次)。
  • name = rhs.name; age = rhs.age;
    rhs对象的成员变量复制给当前对象。这里是浅拷贝,但对于string类成员,它内部会自己管理深拷贝。
  • return *this;
    返回当前对象的引用,支持a = b = c;这种连续赋值写法。

什么时候必须重载赋值运算符?

当你的类含有指针成员或动态资源时,默认浅拷贝会导致:

  • 多个对象指向同一块资源,修改一个会影响另一个
  • 对象析构时重复释放同一块资源,程序崩溃

这时你必须写深拷贝版本的赋值运算符

指针成员的赋值运算符(深拷贝)示例

#include <iostream> #include <cstring> using namespace std; class Person { private: char* name; int age; public: Person() : name(nullptr), age(0) {} Person(const char* n, int a) : age(a) { name = new char[strlen(n) + 1]; strcpy(name, n); } // 重载赋值运算符,深拷贝 Person& operator=(const Person& rhs) { if (this == &rhs) // 自我赋值检测 return *this; delete[] name; // 释放旧资源 name = new char[strlen(rhs.name) + 1]; // 分配新内存 strcpy(name, rhs.name); // 复制内容 age = rhs.age; return *this; } void print() { cout << "Name: " << (name ? name : "null") << ", Age: " << age << endl; } ~Person() { delete[] name; } }; int main() { Person p1("Tom", 20); Person p2; p2 = p1; // 调用深拷贝赋值 p2.print(); } 

Read more

人工智能:大模型高效推理与部署技术实战

人工智能:大模型高效推理与部署技术实战

人工智能:大模型高效推理与部署技术实战 1.1 本章学习目标与重点 💡 学习目标:掌握大语言模型推理与部署的核心技术,理解模型量化、推理加速、服务化部署的原理,能够完成开源大模型的高性能生产级部署。 💡 学习重点:精通INT4/INT8量化技术的应用,掌握vLLM等高性能推理框架的使用方法,学会搭建高并发的大模型API服务。 1.2 大模型推理部署的核心挑战 1.2.1 大模型推理的痛点分析 💡 预训练大模型通常具备数十亿甚至上百亿的参数量,直接进行推理会面临显存占用高、推理速度慢、并发能力弱三大核心问题。 * 显存占用高:以LLaMA-2-7B模型为例,FP16精度下显存占用约14GB,单张消费级显卡难以承载;而70B模型FP16精度显存占用更是超过140GB,普通硬件完全无法运行。 * 推理速度慢:自回归生成的特性导致模型需要逐token计算,单条长文本生成可能需要数十秒,无法满足实时应用需求。 * 并发能力弱:传统推理方式下,单卡同时处理的请求数极少,高并发场景下会出现严重的排队和延迟问题。 这些问题直接制约了大模型从实验室走向实际生产环境,因此高效

By Ne0inhk
Flutter 组件 tavily_dart 的适配 鸿蒙Harmony 深度进阶 - 驾驭 AI 原生聚合搜索、实现鸿蒙端跨域知识发现与垂直领域语义降噪方案

Flutter 组件 tavily_dart 的适配 鸿蒙Harmony 深度进阶 - 驾驭 AI 原生聚合搜索、实现鸿蒙端跨域知识发现与垂直领域语义降噪方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 tavily_dart 的适配 鸿蒙Harmony 深度进阶 - 驾驭 AI 原生聚合搜索、实现鸿蒙端跨域知识发现与垂直领域语义降噪方案 前言 在前文中,我们领略了 tavily_dart 在鸿蒙(OpenHarmony)生态中实现基础互联网 AI 搜索集成的魅力。但在真正的“跨国科研智能辅助”、“政务决策舆情态势感知”以及“需要接入高精密专业数据库”的场景中。简单的单次查询往往不足以触达知识的核心。面对需要在大规模并发环境下,针对特定行业域名(如 .gov / .edu)执行深层内容的并行嗅探,并且要求对回显的数万字内容执行基于 AI 强语义的重排序(Re-ranking)与引用链路审计的高阶需求。如果缺乏一套完善的聚合搜索策略与语义降噪模型。不仅会导致 AI 智能体出现由于“信息泛滥”

By Ne0inhk
【Unity-AI开发篇】| Unity-MCP最新指南:让AI接管游戏开发

【Unity-AI开发篇】| Unity-MCP最新指南:让AI接管游戏开发

* 前言 * 【Unity-AI开发篇】| Unity-MCP最新指南:让AI接管游戏开发 * 一、🧐 MCP是什么? * 1.1 MCP介绍 * 1.2 为什么要配置MCP? * 1.3 效果展示 * 1.4 使用说明及下载 * 二、🚀MCP安装步骤 * 2.1 前提条件 * 2.2 安装 Unity-MCP包(桥接组件) * 2.2 MCP配置 * 三、🎈Trae配置 * 3.1 添加MCP配置 * 3.2 创建一个智能体并添加Unity-MCP * 3.3 使用AI开发功能 * 总结 前言 * 在人工智能飞速发展的今天,大语言模型早已不仅限于聊天和文本生成。 * 它们开始能够使用工具,与环境进行交互,从而执行复杂任务。 * 对于广大游戏开发者而言,

By Ne0inhk
2026本地AI Agent王炸组合:Qwen3.5+OpenClaw,30分钟打造你的24小时赛博打工人

2026本地AI Agent王炸组合:Qwen3.5+OpenClaw,30分钟打造你的24小时赛博打工人

文章目录 * 前言 * 一、先搞明白:啥是长了手脚的AI? * 二、Qwen3.5:不只是参数大,而是「眼力见儿」好 * 三、OpenClaw:给AI装上「手脚」的脚手架 * 四、实战:30分钟搭一个「赛博秘书」 * 4.1 环境准备:两条路线任选 * 路线A:本地Ollama部署(零费用,适合N卡用户) * 路线B:阿里云百炼云端(即开即用,适合A卡/集显用户) * 4.2 安装OpenClaw本体 * 4.3 配置「大脑」连接 * 4.4 给AI「装手装脚」 * 五、玩法进阶:从「问答」

By Ne0inhk