C++核心编程运算符的重载

C++核心编程运算符的重载

文章目录

      • C++核心编程运算符的重载
        • 1.“+”运算符的重载
          • 1.1 作为成员函数重载
          • 1.2 作为全局函数重载
        • 2."<<"运算符重载
          • 2.1为什么需要重载左移运算符
          • 2.2如何重载左移运算符
          • 2.3注意事项
        • 3."++"运算符重载
          • 3.1 前置递增运算符重载
          • 3.2后置递增运算符重载
          • 3.3注意事项
        • 4.赋值运算符重载
          • 4.1解释
          • 4.2注意事项
        • 5.关系运算符的重载
          • 5.1解释
          • 5.2关键点
        • 6.函数调用运算符重载
          • 6.1示例
          • 6.2注意事项

运算符重载在C++等编程语言中是一种重要的特性,它允许程序员为已存在的运算符提供额外的或定制的行为,尤其是针对用户自定义类型(如类和结构体)。以下是几个关键原因,解释了为什么需要运算符重载:

  1. 提升代码的自然度和可读性:通过运算符重载,可以让自定义类型像内置类型一样使用熟悉的运算符。例如,如果你定义了一个复数类,重载加号运算符(+)可以让复数相加就像普通整数或浮点数相加一样自然,这使得代码更易于阅读和理解。

  2. 增强表达能力:运算符重载能够以紧凑的形式表达复杂的操作,避免了使用长而晦涩的函数名称。这样可以让代码更加简洁,减少编程时的认知负担。

  3. 保持一致性:对于用户自定义类型,如果不能重载运算符,那么在处理这些类型时,可能需要引入全新的方法或函数来完成类似内置类型的运算,这会破坏语言使用的一致性体验。

  4. 支持泛型编程:运算符重载是实现模板和泛型算法的关键,它允许算法以统一的方式处理多种类型,只要这些类型支持相应的运算符。

  5. 提高效率:在某些情况下,通过精心设计的运算符重载,可以避免不必要的对象复制,减少临时对象的创建,从而提升程序的运行效率。

  6. 适应面向对象编程:在面向对象编程中,类和对象经常需要进行比较、组合等操作,运算符重载使得这些操作可以直接利用运算符,而不是通过复杂的函数调用来实现,更加符合面向对象的设计理念。

运算符重载是实现代码高效、清晰、一致的重要机制,特别是在处理自定义类型时,它极大地增强了语言的表达能力和灵活性。

1.“+”运算符的重载

在C++中,加号运算符(+)的重载允许你自定义当加号应用于自定义类型时的行为。你可以通过两种方式重载加号运算符:作为成员函数或作为友元函数(全局函数)。下面是两种方式的概览和示例:

1.1 作为成员函数重载

当作为成员函数重载时,加号运算符接受一个参数,这个参数是你要与当前对象相加的对象。返回值通常是该操作的结果,通常是一个新对象或者引用。

class MyClass {
public:// 成员函数重载加号运算符MyClass operator+(const MyClass& other) {MyClass result;// 执行相加操作,例如:result.value = this->value + other.value;return result;}private:int value;
};int main() {MyClass obj1(10);MyClass obj2(20);MyClass sum = obj1 + obj2; // 使用重载的加号运算符// ...
}

示例2:

//
// Created by 86189 on 2024/6/10.
//
#include "iostream"
using namespace std;class  Complex {
public:int real;int imag;Complex operator+(Complex &c2) const{Complex c3{};c3.real = this->real + c2.real;c3.imag = this->imag + c2.imag;return c3;}
};int main() {Complex c1{}, c2{} , c3{};c1.real = 10;c1.imag = 20;c2.real = 5;c2.imag = 10;c3 = c1 + c2;cout << c3.real << endl;cout << c3.imag << endl;return 0;
}
1.2 作为全局函数重载

作为全局函数(友元函数)重载时,加号运算符接受两个参数,这两个参数都是要相加的对象。这种方式的好处是可以访问第一个对象的私有或保护成员。

class MyClass {
public:int value;// 声明为友元函数,可以在类外部定义friend MyClass operator+(const MyClass& lhs, const MyClass& rhs);
};// 全局函数实现加号运算符重载
MyClass operator+(const MyClass& lhs, const MyClass& rhs) {MyClass result;result.value = lhs.value + rhs.value;return result;
}int main() {MyClass obj1{10};MyClass obj2{20};MyClass sum = obj1 + obj2; // 同样使用重载的加号运算符// ...
}

示例2:

//
// Created by 86189 on 2024/6/10.
//
#include <iostream>
using namespace std;class A {
public:int a;int b;
};
A operator+(A &a, A &b) {A c{};c.a = a.a + b.a;c.b = a.b + b.b;return c;
}int main() {A a1{}, a2{}, a3{};a1.a = 1;a1.b = 2;a2.a = 3;a2.b = 4;a3 = a1 + a2;cout << a3.a << " " << a3.b << endl;return 0;
}

无论哪种方式,重载的加号运算符都应当遵循运算符的一般语义和预期行为,确保操作的直观性和代码的可读性。同时,需要注意的是,当涉及自增(++)、自减(--)、赋值(=)等运算符时,还需要考虑前置与后置版本以及可能需要的拷贝构造或移动构造等细节。

2."<<"运算符重载

在C++中,重载运算符是一种特殊函数,它用于给已有运算符提供自定义的行为,特别是针对用户自定义类型。左移运算符(<<)通常与输入/输出流(如std::cout)一起使用来进行数据的输出。但当你想要自定义类型也能支持这样的输出操作时,就需要重载这个运算符。

2.1为什么需要重载左移运算符

当你定义了一个新的类或结构体,并希望像基本数据类型那样方便地将其实例打印出来时,就需要重载<<运算符。这在调试、日志记录和用户界面显示等方面非常有用。

2.2如何重载左移运算符

以下是一个简单的例子,展示了如何为一个自定义的类MyClass重载左移运算符:

#include <iostream>class MyClass {
public:int value;// 构造函数MyClass(int v) : value(v) {}// 左移运算符重载函数friend std::ostream& operator<<(std::ostream& os, const MyClass& obj);
};// 实现左移运算符重载函数
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {os << "MyClass object with value: " << obj.value;return os;
}int main() {MyClass obj(10);std::cout << obj << std::endl;  // 调用重载后的<<运算符输出obj的内容return 0;
}

在这个例子中,我们定义了一个MyClass,它有一个成员变量value。然后,我们声明了一个友元函数来重载<<运算符,该函数接受一个输出流对象(std::ostream&)和一个MyClass的常引用作为参数。在函数内部,我们将MyClass对象的value成员插入到输出流中。通过返回os,我们可以链式调用这个运算符。

2.3注意事项
  • 友元函数:在这里,我们将运算符重载函数声明为类的友元,以便它可以直接访问类的私有和保护成员。
  • 返回值:重载函数应该返回输出流对象的引用,这样就可以支持连续输出(例如 std::cout << obj1 << obj2;)。
  • const引用:传递对象作为const引用是为了避免复制,提高效率,并允许对const对象进行操作。

通过这种方式,你可以使自定义类型更自然、更方便地融入C++的标准I/O机制中。

重载的一般方式:

//
// Created by 86189 on 2024/6/11.
//
#include "iostream"
using namespace std;class MyClass {
public:int a;int b;MyClass(int a, int b) {this->a = a;this->b = b;}
};
ostream &operator<<(ostream &out, MyClass &myClass) {out << myClass.a << " " << myClass.b;return out;
}int main() {MyClass myClass(1, 2);cout << myClass;return 0;
}

一般情况下的全局函数重载,但这种方式只能访问公有的成员属性和方法(函数)。

3."++"运算符重载
3.1 前置递增运算符重载

前置递增运算符直接对对象进行修改并返回修改后的对象本身。通常,它的声明和定义如下:

class MyClass {
public:// 前置递增运算符重载MyClass& operator++() {// 在这里实现自增逻辑// 例如,如果MyClass代表一个数值,可以简单地增加其值++value; // 假设value是类的一个成员变量return *this; // 返回当前对象的引用}
private:int value;
};
3.2后置递增运算符重载

后置递增运算符需要创建一个临时对象来保存自增前的状态,然后原对象再进行自增操作。C++通过接受一个额外的无关参数(通常是int类型的占位符)来区分前置和后置版本:

class MyClass {
public:// 后置递增运算符重载MyClass operator++(int) { // int参数是后置递增的标志MyClass temp(*this); // 创建临时对象保存当前状态++(*this); // 调用前置递增运算符实现自增return temp; // 返回自增前的对象状态}
private:int value;
};
3.3注意事项
  • 确保递增运算符合乎预期,特别是对于复合数据类型或有特殊逻辑的对象。
  • 由于后置递增需要复制当前对象状态,因此在性能敏感的应用中应谨慎使用。
  • 保持运算符重载的一致性,即前置和后置版本在效果上应等价(除了返回类型和是否立即修改对象外)。

通过上述方式,就可以自定义类对象在使用递增运算符时的行为,使其更符合类的设计意图和使用场景。

递增运算符重载的实际应用:

#include <iostream>
using namespace std;class Complex {
public:int value;explicit Complex(int value) {this->value = value;}Complex& operator++() {++this->value;return *this;}
};class Complex2 {
public:int value;explicit Complex2(int value) {this->value = value;}Complex2 operator++(int) {Complex2 temp(*this);this->value++;return temp;}
};
ostream &operator<<(ostream &out, const Complex c) {out << c.value;return out;
}
ostream &operator<<(ostream &out, const Complex2 c) {out << c.value;return out;
}
int main() {Complex c(1);cout << ++c << endl;cout << c << endl;Complex2 c2(1);cout << c2++ << endl;cout << c2 << endl;return 0;
}
4.赋值运算符重载

在C++中,赋值运算符(=, assignment operator)可以被重载,以自定义类对象之间赋值的行为。当你想要控制一个类实例如何将值赋给另一个类实例时,重载赋值运算符变得尤为重要。下面是一个简单的示例,展示了如何重载赋值运算符:

#include <iostream>class MyClass {
public:// 成员变量int value;// 默认构造函数MyClass(int v = 0) : value(v) {}// 赋值运算符重载MyClass& operator=(const MyClass& other) {// 防止自我赋值if (this != &other) {// 实现赋值逻辑value = other.value;}// 返回当前对象的引用,支持连续赋值 (a = b = c)return *this;}
};int main() {MyClass obj1(10);MyClass obj2;std::cout << "Before assignment: obj1.value = " << obj1.value << ", obj2.value = " << obj2.value << std::endl;// 使用重载的赋值运算符obj2 = obj1;std::cout << "After assignment: obj1.value = " << obj1.value << ", obj2.value = " << obj2.value << std::endl;return 0;
}
4.1解释
  • 成员变量MyClass中有一个成员变量value
  • 默认构造函数:提供了默认构造方式,可以初始化value为0或指定的值。
  • 赋值运算符重载:定义了operator=函数,它接受一个对同类对象的引用other作为参数。
    • 自我赋值检查:通过比较this(当前对象地址)和&other(传入对象地址)来判断是否是自我赋值,这是为了避免在自我赋值情况下出现不必要的操作或错误。
    • 赋值逻辑:如果不是自我赋值,就将other.value的值赋予当前对象的value
    • 返回值:重载的赋值运算符通常返回一个对当前对象(*this)的引用,这样可以支持连续赋值操作。
4.2注意事项
  • 深拷贝与浅拷贝:当类中有指针或动态分配的资源时,必须小心处理深拷贝(复制资源的内容)与浅拷贝(只复制指针)的问题,以避免悬挂指针或资源泄露。
  • 复制与交换:有时使用“复制并交换”(copy-and-swap)技术来实现赋值运算符,这是一种既安全又高效的方法。
  • 三/五法则:如果重载了赋值运算符,一般也建议重载拷贝构造函数、析构函数、移动构造函数和移动赋值运算符,以保持类的完整性和资源管理的一致性。这被称为C++的“三/五法则”。
5.关系运算符的重载
5.1解释

在C++中,你可以重载关系运算符(如==, !=, <, >, <=, >=)来定义它们在自定义类型上的行为。重载关系运算符可以帮助你更直观地比较自定义类的实例。下面是一个简单的例子,展示了如何为一个简单的Point类重载==<运算符:

#include <iostream>class Point {
public:int x, y;// 构造函数Point(int x = 0, int y = 0) : x(x), y(y) {}// 重载等于运算符bool operator==(const Point& other) const {return (x == other.x) && (y == other.y);}// 重载小于运算符bool operator<(const Point& other) const {if (y == other.y) {return x < other.x;} else {return y < other.y;}}
};// 为了支持cout输出,重载<<运算符
std::ostream& operator<<(std::ostream& os, const Point& p) {os << "(" << p.x << ", " << p.y << ")";return os;
}int main() {Point p1(1, 2);Point p2(1, 2);Point p3(2, 1);std::cout << "p1 == p2: " << (p1 == p2) << std::endl; // 应输出1,表示真std::cout << "p1 < p3: " << (p1 < p3) << std::endl;   // 应输出0,表示假,因为我们定义了先按y比较,后按x比较return 0;
}
5.2关键点
  • 重载函数:重载的关系运算符通常被声明为类的成员函数(对于非成员函数,需要定义为友元函数以访问私有成员),并且通常被声明为const,因为它们不应该修改对象的状态。
  • 返回类型:重载的关系运算符应该返回bool类型,表示比较的结果。
  • 逻辑一致性:当你重载一个关系运算符时,应确保所有相关运算符(如<>)的逻辑一致,以避免违反关系运算符的传递性、对称性等属性。
  • 友元函数:对于非成员函数形式的关系运算符重载(如==),通常将其声明为类的友元函数,以便直接访问类的私有和保护成员。

重载关系运算符可以让你的自定义类型更加自然地融入C++的标准表达式语法中,提高代码的可读性和易用性。

6.函数调用运算符重载

在C++中,直接重载圆括号()操作符通常是用来实现类的实例作为函数的调用,这种机制常用于仿函数(functor)、智能指针或者任何希望像函数一样被调用的类。重载圆括号操作符是通过在类中定义一个名为operator()的成员函数来实现的。

6.1示例

下面是一个简单的示例,展示了如何定义一个类来重载圆括号操作符,使其实例可以像函数一样被调用:

#include <iostream>class Functor {
public:// 重载圆括号操作符int operator()(int x, int y) {return x + y; // 这个操作符现在像一个求和函数}
};int main() {Functor adder; // 创建类的实例// 使用类实例像函数一样调用int result = adder(10, 20); std::cout << "Result: " << result << std::endl; // 输出: Result: 30return 0;
}

在这个例子中,Functor类通过重载operator(),使得创建的adder对象可以像函数那样被调用,传入两个整数参数,并返回它们的和。

以及使用匿名对象调用:

#include <iostream>
using namespace std;class myClass{
public:myClass(int a, int b) {this->a = a;this->b = b;}myClass(int a) {this->a = a;this->b = 0;}myClass() {this->a = 0;this->b = 0;}void operator()(const string& s){cout << s << endl;}int operator()(int i, int j){return i + j;}
private:int a;int b;};int main() {myClass myClass1(1, 2);myClass1("hello");cout << myClass()(1, 2) << endl; //匿名对象调用return 0;
}
6.2注意事项
  • 返回类型operator()可以有任意合法的返回类型,根据实际需求定义。
  • 参数列表:圆括号内的参数列表也可以根据需要自由定义,就像普通函数的参数列表一样。
  • 多态性:重载的operator()可以实现多态行为,使得类的实例能够以统一的方式处理不同类型的输入。

通过重载圆括号操作符,C++提供了强大的灵活性,允许用户定义能够像函数一样被调用的对象,这对于函数对象(functors)、函数适配器、策略模式等设计模式非常有用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/31647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

添加右键菜单(以git为例)

1、打开注册表编辑器 打开系统注册表&#xff0c;使用组合键“Win R”输入“regedit”。 依次展开”HKEY_CLASSES_ROOT\Directory\Background\shell”。 2、新建右键菜单项 在[Background]下找到“shell”如果没有则新建项shell&#xff0c;接着在“shell”下右键-新建项名…

算法训练营day66-孤岛总面积-沉没孤岛-水流问题-建造最大岛屿

题目1&#xff1a;101. 孤岛的总面积 (kamacoder.com) #include <iostream> #include <vector> #include <queue> using namespace std; int dir[4][2] {0,-1,-1,0,0,1,1,0}; int count 0; void bfs(vector<vector<int>>& map, vector<…

智能农业管理系统设计

一、引言 随着物联网、云计算和大数据技术的快速发展&#xff0c;智能农业管理系统成为提高农业生产效率、优化资源配置、降低环境污染的重要手段。本设计旨在构建一个集数据采集、传输、处理、分析于一体的智能农业管理系统&#xff0c;为农业生产提供全方位、精准化的服务。 …

基于DPU的云原生裸金属网络解决方案

1. 方案背景和挑战 裸金属服务器是云上资源的重要部分&#xff0c;其网络需要与云上的虚拟机和容器互在同一个VPC下&#xff0c;并且能够像容器和虚拟机一样使用云的网络功能和能力。 传统的裸金属服务器使用开源的 OpenStack Ironic 组件&#xff0c;配合 OpenStack Neutron…

修改主频睡眠模式停止模式待机模式

代码示例&#xff1a; 接线图&#xff1a;修改主频 接线图&#xff1a;睡眠模式串口发送接收 CH340 USB转串口模块。GND和stm32共地。RXD接PA9&#xff0c;TXD接PA10。 接线图&#xff1a;停止模式对射式红外传感器计次 对射式红外传感器模块的VCC和GND接上供电。DO输出接S…

张大哥笔记:5种信息差赚钱模式

从古至今&#xff0c;赚钱最快的路子就一个&#xff0c;而且从未改变&#xff0c;那就是信息差&#xff01;在商业活动中&#xff0c;信息不对称现象普遍存在&#xff0c;如果你善于利用这些信息差的话&#xff0c;就可以赚到钱&#xff01; 1、价格的信息差 商品价格在不同地…

python pyautogui实现图片识别点击失败后重试

安装库 pip install Pillow pip install opencv-python confidence作用 confidence 参数是用于指定图像匹配的信度&#xff08;或置信度&#xff09;的&#xff0c;它表示图像匹配的准确程度。这个参数的值在 0 到 1 之间&#xff0c;数值越高表示匹配的要求越严格。 具体来…

ConcurrentHashMap(应对并发问题的工具类)

并发工具类 在JDK的并发包里提供了几个非常有用的并发容器和并发工具类。供我们在多线程开发中进行使用。 5.1 ConcurrentHashMap 5.1.1 概述以及基本使用 在集合类中HashMap是比较常用的集合对象&#xff0c;但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了…

可一件转化的视频生成模型:快手官方大模型“可灵”重磅来袭!

可一件转化的视频生成模型“可灵”重磅来袭&#xff01; 前言 戴墨镜的蒙娜丽莎 达芬奇的画作《蒙娜丽莎的微笑》相信大家是在熟悉不过了&#xff0c;可《戴墨镜的蒙娜丽莎》大家是不是第一次见&#xff1f;而且这还不是以照片的形式&#xff0c;而是以视频的形式展示给大家。 …

Spring AOP实战--之优雅的统一打印web请求的出参和入参

背景介绍 由于实际项目内网开发&#xff0c;项目保密&#xff0c;因此本文以笔者自己搭建的demo做演示&#xff0c;方便大家理解。 在项目开发过程中&#xff0c;团队成员为了方便调试&#xff0c;经常会在方法的出口和入口处加上log输出&#xff0c;由于每个人的log需求和输…

奔驰EQS SUV升级原厂主动式氛围灯效果展示

以下是一篇关于奔驰 EQs 升级原厂主动氛围灯案例的宣传文案&#xff1a; 在汽车科技不断演进的今天&#xff0c;我们自豪地为您呈现奔驰 EQs 升级原厂主动氛围灯的精彩案例。 奔驰 EQs&#xff0c;作为豪华电动汽车的典范&#xff0c;其卓越品质与高端性能有目共睹。而此次升…

CVPR 2024盛况空前,上海科技大学夺得最佳学生论文奖,惊艳全场

CVPR 2024盛况空前&#xff01;上海科技大学夺得最佳学生论文奖&#xff0c;惊艳全场&#xff01; 会议之眼 快讯 2024 年 CVPR &#xff08;Computer Vision and Pattern Recogntion Conference) 即国际计算机视觉与模式识别会议&#xff0c;于6月17日至21日正在美国西雅图召…

手把手教你java CPU飙升300%如何优化

背景 今天有个项目运行一段时间后&#xff0c;cpu老是不堪负载。 排查 top 命令 TOP 命令 top t 按cpu 排序 top m 按内存使用率排序 从上面看很快看出是 pid 4338 这个进程资源消耗很高。 top -Hp pid top -Hp 4338 找到对应线程消耗的资源shftp cpu占用进行排序&#xf…

【Java】已解决java.net.ProtocolException异常

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.net.ProtocolException异常 在Java的网络编程中&#xff0c;java.net.ProtocolException异常通常表示在网络通信过程中&#xff0c;客户端或服务器违反了某种协议规则。…

Java 中引用类型的参数传递

了解 Java 中引用类型的参数传递 Java 是一种面向对象的编程语言&#xff0c;它的参数传递机制一直是新手和有经验的开发者之间讨论的热点话题。特别是当涉及到引用类型时&#xff0c;理解其工作原理变得尤为重要。下面我们将详细探讨 Java 中引用类型的参数传递机制。 Java …

计算机组成原理 | 计算机系统概述

CPI:(Clockcycle Per Instruction)&#xff0c;指每条指令的时钟周期数。 时钟周期&#xff1a;对CPU来说&#xff0c;在一个时钟周期内&#xff0c;CPU仅完成一个最基本的动作。时钟脉冲是计算机的基本工作脉冲&#xff0c;控制着计算机的工作节奏。时钟周期 是一个时钟脉冲所…

【2024Python教程】-MongoDB数据库连接

MongoDB数据库连接实战 MongoDB数据库连接 首先&#xff0c;通过以下代码连接到本地的MongoDB: client MongoClient(mongodb://localhost:27017/)然后&#xff0c;创建或切换到名为pdf_contents的数据库: db client[pdf_contents]在pdf_contents数据库中,创建或切换到名为…

除了百度,还有哪些搜索引擎工具可以使用

搜索引擎成是我们获取知识和信息不可或缺的工具。百度作为国内最大的搜索引擎&#xff0c;全球最大的中文搜索引擎&#xff0c;是许多人的首选。那么除了百度&#xff0c;还有哪些搜索引擎可以使用呢&#xff1f;小编就来和大家分享国内可以使用的其他搜索工具。 1. AI搜索 AI…

微信公众号开发,uploadImg上传图片接口40001错误解决办法

使用微信公众平台&#xff0c;公众号接口uploadImg上传图片&#xff0c;的时候&#xff0c;access_token明明是对的&#xff0c;appsecret也是对的&#xff0c;但是一直返回40001错误&#xff0c;获取access_token时AppSecret错误&#xff0c;或者access_token无效&#xff0c;…

梯度提升决策树(GBDT)的训练过程

以下通过案例&#xff08;根据行为习惯预测年龄&#xff09;帮助我们深入理解梯度提升决策树&#xff08;GBDT&#xff09;的训练过程 假设训练集有4个人&#xff08;A、B、C、D&#xff09;&#xff0c;他们的年龄分别是14、16、24、26。其中A、B分别是高一和高三学生&#x…