[知识点]c++运算符重载

在 C++ 中,运算符重载(Operator Overloading)允许你定义或修改运算符的行为,使其适用于用户定义的类型(例如类或结构体)。通过运算符重载,你可以使自定义类型与内置类型一样自然地使用运算符。

重载运算符的基本原则

  1. 不能创建新的运算符:只能重载已有的 C++ 运算符。
  2. 不能改变运算符的优先级和结合性:重载运算符的优先级和结合性与原运算符相同。
  3. 必须有一个用户定义类型的操作数:至少有一个操作数是用户定义类型(类或结构体)。
  4. 某些运算符不能被重载:例如,::(作用域解析运算符)、.*(成员指针访问运算符)、.(成员访问运算符)、?:(三元运算符)等。

运算符重载的方法

运算符可以作为成员函数或非成员函数(通常是友元函数)进行重载。

成员函数重载
  • 一元运算符:无参数。
  • 二元运算符:一个参数。
非成员函数(友元函数)重载
  • 一元运算符:一个参数。
  • 二元运算符:两个参数。

常见运算符重载示例

1. 重载赋值运算符 =

赋值运算符通常作为成员函数重载,用于实现深拷贝。

#include <iostream>
#include <cstring>class String {
private:char* data;
public:String(const char* str = "") {data = new char[strlen(str) + 1];strcpy(data, str);}String(const String& other) {data = new char[strlen(other.data) + 1];strcpy(data, other.data);}~String() {delete[] data;}String& operator=(const String& other) {if (this != &other) {delete[] data;data = new char[strlen(other.data) + 1];strcpy(data, other.data);}return *this;}void print() const {std::cout << data << std::endl;}
};int main() {String str1("Hello");String str2 = str1;String str3;str3 = str2;str1.print();str2.print();str3.print();return 0;
}
2. 重载算术运算符 +
#include <iostream>class Complex {
private:double real, imag;
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 成员函数重载Complex operator+(const Complex& other) const {return Complex(real + other.real, imag + other.imag);}void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};int main() {Complex c1(3.0, 4.0);Complex c2(1.0, 2.0);Complex c3 = c1 + c2;c3.print();return 0;
}
3. 重载关系运算符 ==
#include <iostream>class Complex {
private:double real, imag;
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 成员函数重载bool operator==(const Complex& other) const {return real == other.real && imag == other.imag;}void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};int main() {Complex c1(3.0, 4.0);Complex c2(3.0, 4.0);if (c1 == c2) {std::cout << "c1 is equal to c2" << std::endl;} else {std::cout << "c1 is not equal to c2" << std::endl;}return 0;
}
4. 重载输入输出运算符 <<>>

输入输出运算符通常作为友元函数重载。

#include <iostream>class Complex {
private:double real, imag;
public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}friend std::ostream& operator<<(std::ostream& os, const Complex& c) {os << "(" << c.real << ", " << c.imag << ")";return os;}friend std::istream& operator>>(std::istream& is, Complex& c) {is >> c.real >> c.imag;return is;}
};int main() {Complex c1;std::cout << "Enter a complex number (real and imaginary part): ";std::cin >> c1;std::cout << "You entered: " << c1 << std::endl;return 0;
}

5. 重载数组下标运算符 []

#include <iostream>class Array {
private:int* data;size_t size;
public:Array(size_t sz) : size(sz) {data = new int[size];}~Array() {delete[] data;}int& operator[](size_t index) {return data[index];}const int& operator[](size_t index) const {return data[index];}size_t getSize() const {return size;}
};int main() {Array arr(10);for (size_t i = 0; i < arr.getSize(); ++i) {arr[i] = i * 10;}for (size_t i = 0; i < arr.getSize(); ++i) {std::cout << arr[i] << " ";}std::cout << std::endl;return 0;
}

6. 重载前置和后置递增递减运算符 ++--

#include <iostream>class Counter {
private:int value;
public:Counter(int val = 0) : value(val) {}// 前置递增Counter& operator++() {++value;return *this;}// 后置递增Counter operator++(int) {Counter temp = *this;++value;return temp;}// 前置递减Counter& operator--() {--value;return *this;}// 后置递减Counter operator--(int) {Counter temp = *this;--value;return temp;}int getValue() const {return value;}
};int main() {Counter c(5);std::cout << "Initial value: " << c.getValue() << std::endl;++c;std::cout << "After prefix increment: " << c.getValue() << std::endl;c++;std::cout << "After postfix increment: " << c.getValue() << std::endl;--c;std::cout << "After prefix decrement: " << c.getValue() << std::endl;c--;std::cout << "After postfix decrement: " << c.getValue() << std::endl;return 0;
}

友元函数的作用
友元函数可以访问类的私有和保护成员,因此可以实现非成员函数的灵活性,同时保留对类内部状态的访问能力。

何时需要友元函数重载
对称性:对于某些运算符,尤其是二元运算符(如 +, -, *, /, ==, != 等),将运算符重载为友元函数可以实现更好的对称性,允许左操作数和右操作数分别是类对象和非类对象。例如,a + b 和 b + a 需要对称处理。
访问私有成员:当运算符需要访问类的私有或保护成员时,可以将其定义为友元函数,以便访问这些成员。
非成员函数:某些运算符(如输入输出运算符 << 和 >>)通常需要定义为非成员函数,因为左操作数是标准库类型(如 std::ostream 或 std::istream),不能作为类的成员函数来实现。

总结

将运算符重载为成员函数或友元函数都会对类的设计和使用产生不同的影响。理解这些影响可以帮助你做出最佳选择。以下是将运算符重载为成员函数和友元函数的一些影响和考虑。

成员函数重载的影响

  1. 调用方式的限制
  2. 访问权限
  3. 对称性
  4. 简化实现

1. 调用方式的限制 (主要差别)

成员函数重载运算符的一个主要限制是,它只能在类的实例作为左操作数时调用。这是因为成员函数隐含地接受类实例作为第一个参数。

示例:成员函数重载 +
#include <iostream>class Complex {
private:double real, imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 成员函数重载 + 运算符Complex operator+(const Complex& rhs) const {return Complex(real + rhs.real, imag + rhs.imag);}void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};int main() {Complex c1(3.0, 4.0);Complex c2(1.0, 2.0);Complex c3 = c1 + c2; // 合法,c1 是左操作数// Complex c4 = 1.0 + c1; // 非法,左操作数不是 Complex 类实例c3.print(); // 输出: (4.0, 6.0)return 0;
}

在这个例子中,c1 + c2 是合法的,因为 c1Complex 类的实例。但 1.0 + c1 是非法的,因为 1.0 不是 Complex 类的实例。

2. 访问权限

成员函数重载可以直接访问类的私有和保护成员,无需通过公共接口。这在某些情况下可以简化实现。

示例:成员函数重载 + 访问私有成员
#include <iostream>class Complex {
private:double real, imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 成员函数重载 + 运算符Complex operator+(const Complex& rhs) const {return Complex(real + rhs.real, imag + rhs.imag);}void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};int main() {Complex c1(3.0, 4.0);Complex c2(1.0, 2.0);Complex c3 = c1 + c2;c3.print(); // 输出: (4.0, 6.0)return 0;
}

3. 对称性

对于需要对称处理的运算符(如 +, == 等),友元函数提供了更好的对称性,因为它们可以对任意顺序的操作数进行操作

示例:友元函数重载 + 支持对称性
#include <iostream>class Complex {
private:double real, imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 友元函数重载 + 运算符friend Complex operator+(const Complex& lhs, const Complex& rhs);void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};// 友元函数实现 + 运算符
Complex operator+(const Complex& lhs, const Complex& rhs) {return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
}int main() {Complex c1(3.0, 4.0);Complex c2(1.0, 2.0);Complex c3 = c1 + c2;Complex c4 = c2 + c1; // 同样合法c3.print(); // 输出: (4.0, 6.0)c4.print(); // 输出: (4.0, 6.0)return 0;
}

4. 简化实现

对于一些运算符来说,成员函数实现比友元函数实现更简洁,因为它们不需要显式地访问私有成员

示例:成员函数重载 ==
#include <iostream>class Complex {
private:double real, imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 成员函数重载 == 运算符bool operator==(const Complex& rhs) const {return real == rhs.real && imag == rhs.imag;}void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};int main() {Complex c1(3.0, 4.0);Complex c2(3.0, 4.0);Complex c3(1.0, 2.0);if (c1 == c2) {std::cout << "c1 is equal to c2" << std::endl; // 输出} else {std::cout << "c1 is not equal to c2" << std::endl;}if (c1 == c3) {std::cout << "c1 is equal to c3" << std::endl;} else {std::cout << "c1 is not equal to c3" << std::endl; // 输出}return 0;
}

友元函数的优势

友元函数可以用于解决上述成员函数重载的限制,例如支持非对称操作数和更好的对称性。

示例:友元函数重载 + 支持非类对象
#include <iostream>class Complex {
private:double real, imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 友元函数重载 + 运算符friend Complex operator+(const Complex& lhs, const Complex& rhs);// 友元函数重载 Complex + doublefriend Complex operator+(const Complex& lhs, double rhs);// 友元函数重载 double + Complexfriend Complex operator+(double lhs, const Complex& rhs);void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};// 友元函数实现 Complex + Complex
Complex operator+(const Complex& lhs, const Complex& rhs) {return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
}// 友元函数实现 Complex + double
Complex operator+(const Complex& lhs, double rhs) {return Complex(lhs.real + rhs, lhs.imag);
}// 友元函数实现 double + Complex
Complex operator+(double lhs, const Complex& rhs) {return Complex(lhs + rhs.real, rhs.imag);
}int main() {Complex c1(3.0, 4.0);Complex c2(1.0, 2.0);Complex c3 = c1 + c2;Complex c4 = c1 + 5.0;Complex c5 = 5.0 + c1;c3.print(); // 输出: (4.0, 6.0)c4.print(); // 输出: (8.0, 4.0)c5.print(); // 输出: (8.0, 4.0)return 0;
}
  • 成员函数重载:适用于一元运算符或当左操作数必须是类实例时。成员函数重载可以直接访问类的私有成员。
  • 友元函数重载:适用于需要对称性、需要访问类的私有成员以及需要支持非类对象作为操作数的运算符。友元函数可以访问类的私有和保护成员。

使用模板进行运算符重载

使用模板可以实现更加通用的运算符重载方案,适用于多种类型的相加操作。
示例:使用模板重载 + 运算符

在这里插入代码片#include <iostream>
#include <type_traits>class Complex {
private:double real, imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}// 友元函数模板重载 + 运算符,支持 Complex + Ttemplate<typename T>friend typename std::enable_if<!std::is_same<T, Complex>::value, Complex>::typeoperator+(const Complex& lhs, const T& rhs) {return Complex(lhs.real + rhs, lhs.imag);}// 友元函数模板重载 + 运算符,支持 T + Complextemplate<typename T>friend typename std::enable_if<!std::is_same<T, Complex>::value, Complex>::typeoperator+(const T& lhs, const Complex& rhs) {return Complex(lhs + rhs.real, rhs.imag);}// 友元函数重载 Complex + Complexfriend Complex operator+(const Complex& lhs, const Complex& rhs);void print() const {std::cout << "(" << real << ", " << imag << ")" << std::endl;}
};// 友元函数实现 Complex + Complex
Complex operator+(const Complex& lhs, const Complex& rhs) {return Complex(lhs.real + rhs.real, lhs.imag + rhs.imag);
}int main() {Complex c1(3.0, 4.0);Complex c2(1.0, 2.0);Complex c3 = c1 + c2;Complex c4 = c1 + 5.0;Complex c5 = 5.0 + c1;c3.print();  // 输出: (4.0, 6.0)c4.print();  // 输出: (8.0, 4.0)c5.print();  // 输出: (8.0, 4.0)return 0;
}

在这个示例中,我们使用了模板和 std::enable_if 来实现通用的运算符重载,以支持 Complex 对象与任意类型的值进行相加。通过 std::enable_if 和 std::is_same 检查,确保模板重载仅适用于非 Complex 类型
template:定义一个模板,参数类型为 T。
typename std::enable_if<!std::is_same<T, Complex>::value, Complex>::type:条件性地定义返回类型。
std::is_same<T, Complex>::value:检查类型 T 是否与 Complex 类型相同。
!std::is_same<T, Complex>::value:取反,检查类型 T 是否不等于 Complex。
std::enable_if<!std::is_same<T, Complex>::value, Complex>::type:如果 T 不是 Complex 类型,则 std::enable_if 定义了一个类型 type,该类型为 Complex。否则,模板实例化失败。

运算符重载使得自定义类型的使用更加直观和自然。通过运算符重载,可以实现与内置类型一致的语法和行为,提高代码的可读性和可维护性。在进行运算符重载时,需注意以下几点:

  1. 确保重载的运算符符合预期的行为:重载后的运算符应当符合人们对该运算符的常规预期。
  2. 避免滥用运算符重载:虽然运算符重载可以提高代码的简洁性,但不应滥用,特别是对于不适合运算符语义的操作。
  3. 遵循运算符的优先级和结合性:重载运算符的优先级和结合性与原运算符保持一致。

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

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

相关文章

大模型的 Embedding 模型该如何进行微调?

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

Vue的基础知识:v-model的原理,由:value与@input合写。

原理&#xff1a;v-model本质上是一个语法糖&#xff0c;比如应用在输入框上&#xff0c;就是value属性和input事件的合写。&#xff08;补充说明&#xff1a;语法糖就是语法的简写&#xff09; 作用&#xff1a;提供数据的双向绑定 1.数据变&#xff0c;视图&#xff08;也就…

[数据集][目标检测]叶子计数检测数据集VOC+YOLO格式240张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;240 标注数量(xml文件个数)&#xff1a;240 标注数量(txt文件个数)&#xff1a;240 标注类别…

2024年谷歌SEO如何快速出排名效果抢占首页制高点?

2024年&#xff0c;随着谷歌搜索引擎算法的不断更新&#xff0c;SEO策略也需要与时俱进才能快速出排名。本文将结合谷歌最新SEO趋势&#xff0c;平哥SEO分享一些实操性的快速排名技巧&#xff0c;帮助你在竞争激烈的搜索结果中脱颖而出。 额外话题&#xff1a;就是通过微信公众…

break、continue、return

break 程序示例&#xff1a; // 产生一个位于 [1, 100] 范围内的随机数&#xff0c;统计产生 100 所需要的次数 public static void main(String[] args) {// System.out.println(Math.random()); // [0,1)// System.out.println(Math.random() * 100); // [0,100)// Syste…

什么是XSS攻击?什么是SQL注入攻击?什么是CSRF攻击?

XSS攻击、SQL注入攻击和CSRF攻击是三种常见的网络安全威胁&#xff0c;它们分别针对不同的应用层面和安全漏洞。以下是对这三种攻击方式的详细介绍&#xff1a; 1. XSS攻击&#xff08;跨站脚本攻击&#xff0c;Cross-Site Scripting&#xff09; 业务场景&#xff1a; 用户…

Java Web学习笔记25——Vue组件库Element

什么是Element&#xff1f; Element: 是饿了么团队研发的&#xff0c;一套为开发者、设计师和产品经理准备的基于Vue2.0的桌面端组件库。 组件&#xff1a;组成网页的部件&#xff0c;例如&#xff1a;超链接、按钮、图片、表格、表单、分页条等等。 官网&#xff1a;https:…

C++设计模式---工厂模式

C中工厂模式是一种创建型设计模式&#xff0c;它允许客户端代码通过调用工厂方法来创建对象&#xff0c;而无需直接使用new运算符实例化具体类。这种模式有助于将类的创建与使用相分离&#xff0c;并且在需要添加新的具体类时可以减少对客户端代码的影响。 工厂模式通常有两种实…

深拷贝、浅拷贝、引用拷贝

深拷贝和浅拷贝的区别 1. 引用拷贝2. 对象拷贝 1. 引用拷贝 两个对象指向同一个地址值。 创建一个指向对象的引用变量的拷贝Teacher teacher new Teacher("Taylor",26); Teacher otherteacher teacher; System.out.println(teacher); System.out.println(otherte…

cocos入门8:向量叉乘

在cocos creator中&#xff0c;向量叉乘&#xff08;Cross Product&#xff09;是一个重要的概念&#xff0c;主要用于3D图形学中的方向计算和法线计算。叉乘的结果是一个垂直于两个输入向量的新向量&#xff0c;其长度等于输入向量围成的平行四边形的面积。以下是对向量叉乘的…

前端多人项目开发中,如何保证CSS样式不冲突?

在前端项目开发中&#xff0c;例如突然来了一个大项目&#xff0c;很可能就需要多人一起开发&#xff0c;领导说了&#xff0c;要快&#xff0c;要快&#xff0c;要快&#xff0c;你们给我快。然后下面大伙就一拥而上&#xff0c;干着干着发现&#xff0c;一更新代码&#xff0…

【AI论文与新生技术】Follow-Your-Emoji:精细可控且富有表现力的自由式人像动画技术

我们提出了 Follow-Your-Emoji&#xff0c;这是一种基于扩散的肖像动画框架&#xff0c;它使用目标地标序列对参考肖像进行动画处理。肖像动画的主要挑战是保留参考肖像的身份并将目标表情转移到该肖像&#xff0c;同时保持时间一致性和保真度。为了应对这些挑战&#xff0c;Fo…

JFinal学习07 控制器——接收数据之getBean()和getModel()

JFinal学习07 控制器——接收数据之getBean()和getModel() 视频来源https://www.bilibili.com/video/BV1Bt411H7J9/?spm_id_from333.337.search-card.all.click 文章目录 JFinal学习07 控制器——接收数据之getBean()和getModel()一、接收数据的类型二、getBean()和getModel()…

GDPU JavaWeb Ajax请求

异步请求可以提升用户体验并优化页面性能。 ajax登录 实现ajax异步登录。 注意&#xff0c;ajax用到了jQuery库&#xff0c;先下载好相应的js库&#xff0c;然后复制导入到工程的web目录下&#xff0c;最好与你的前端页面同一层级。然后编写时路径一定要找准&#xff0c;“pag…

WinRAR安装教程

WinRAR安装教程 1. 下载WinRAR 访问官方网站&#xff1a;打开浏览器&#xff0c;访问WinRAR的官方网站&#xff08;如&#xff1a;www.winrar.com.cn&#xff09;。选择版本&#xff1a;根据您的操作系统&#xff08;32位或64位&#xff09;选择合适的WinRAR版本。下载软件&a…

转让北京公司带旅行许可证流程和要求

旅行社是开展旅游服务业务的专项经济主体&#xff0c;旅行社开展相关业务必须持有旅行社业务经营许可证。该资质又分为国内旅行社经营许可证和出境旅行社经营许可证。主要区别在于能否开展出境旅游业务&#xff0c;下面老耿带大家了解&#xff0c;新成立国内旅行社要求及出境旅…

python-windows10普通笔记本跑bert mrpc数据样例0.1.001

python-windows10普通笔记本跑bert mrpc数据样例0.1.000 背景参考章节获取数据下载bert模型下载bert代码windows10的cpu执行结果注意事项TODOLIST背景 看了介绍说可以在gpu或者tpu上去微调,当前没环境,所以先在windows10上跑一跑,看是否能顺利进行,目标就是训练的过程中没…

【Vue2/3】使用Provide/Inject 依赖注入跨组件通信

历史小剧场 什么东西&#xff0c;都有使用年限&#xff0c;比如大米、王朝。 不同的是&#xff0c;大米的年限看得见&#xff0c;王朝的年限看不见。看不见&#xff0c;却依然存在。对于气数&#xff0c;崇祯是不信的&#xff0c;开始不信。等到崇祯十四年&#xff0c;怕什么来…

js--hasOwnProperty()讲解与使用

@TOC 前言 hasOwnProperty(propertyName)方法 是用来检测属性是否为对象的自有属性 object.hasOwnProperty(propertyName) // true/false 讲解 hasOwnProperty() 方法是 Object 的原型方法(也称实例方法),它定义在 Object.prototype 对象之上,所有 Object 的实例对象都会继…

6.7 输入输出流

输入&#xff1a;将数据放到程序&#xff08;内存&#xff09;中 输出&#xff1a;将数据从程序&#xff08;内存&#xff09;放到设备中 C的输入输出分为3种形式&#xff1a; 从键盘屏幕中输入输出&#xff0c;称为标准IO 对于磁盘进行标准输入输出&#xff0c;称为文件IO…