c++--运算符重载

1.重载的运算符
(1).重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。
(2).除了重载的函数调用运算符operator()之外,其他重载运算符不能含有默认实参。
(3).对一个重载的运算符,其优先级和结合律与对应的内置运算符保持一致。
(4).当一个重载的运算符是成员函数时,this绑定到左侧运算对象。故,成员运算符函数的参数数量比运算对象数量少一个。
(5).对一个运算符函数,或者是类的成员,或者至少含一个类类型的参数。

2.运算符函数的调用形式:
// 假设有非成员函数 operator+
显式:operator+(data1, data2);
隐式:data1+data2
// 假设有成员函数 operator+
显式:data1.operator+(data2)
隐式:data1+data2

3.关于将运算符函数定义为成员还是非成员:
赋值(=),下标([]),调用(()),成员访问箭头(->)必须为成员。

4.运算符重载
4.1.重载<<
(1).通常,输出运算符的第一个形参是一个非常量ostream对象的引用。
a.为引用,是因为ostream对象不能被拷贝。
b.非常量是因为,向流写入内容会改变其状态。
(2).第二个形参一般是一个常量的引用,该常量是我们想打印的类类型。
a.是引用,是因为可以避免值拷贝。
b.常量是因为,打印对象不会改变对象的内容。
(3).为与其他输出运算符一致,operator<<一般返回它的ostream形参。
(4).一个实例

#include <iostream>
class A
{
public:int m_a;int m_b;
};std::ostream &operator<<(std::ostream &os, const A&item)
{os << item.m_a << " " << item.m_b;return os;
}

4.2.重载>>
(1).通常,输入运算符的第一个形参是运算符将要读取的流的引用,
(2).第二个形参是将要读入到的非常量对象的引用。
(3).该运算符通常会返回某个给定流的引用。
(4).一个实例

#include <iostream>
class A
{
public:int m_a;int m_b;
};std::istream& operator>>(std::istream &is, A& item)
{char a;is >> item.m_a >> a >> item.m_b;return is;
}

执行输入时,可能发生下列错误:
(1).流含有错误类型的数据时,读取操作可能失败。
(2).读取操作到达文件末尾或遇到输入流的其它错误时也会失败。

4.3.算术运算符
4.3.1.最佳实践
(1).类定义了一个算术运算符时,一般也要定义一个复合赋值运算符。
(2).一般借用复合赋值运算符来定义算术运算符。
(3).通常,算术和关系运算符定义为非成员函数以允许左侧或右侧的运算对象进行转换。
(4).这些运算符一般不需改变运算对象的状态,故形参可以是常量的引用。
(5).算术运算的结果一般位于局部变量内。操作完成一般返回局部变量的副本作为结果。
(6).复合赋值运算符更适合作为类成员来定义。
(7).一个实例

#include <iostream>
class A
{
friend bool operator==(const A &a1, const A &a2);
public:A():m_a(0), m_b(0){}A(int i):m_a(i), m_b(i){}void print(){printf("a_%d,b_%d\n", m_a, m_b);}A& operator+=(const A &a2){m_a = m_a + a2.m_a;m_b = m_b + a2.m_b;return *this;}
private:int m_a;int m_b;
};A operator+(const A &a1, const A &a2)
{A sum = a1;sum += a2;return sum;
}bool operator==(const A &a1, const A &a2)
{return a1.m_a == a2.m_a && a1.m_b == a2.m_b;
}bool operator!=(const A& lhs, const A& rhs)
{return !(lhs == rhs);
}int main()
{A a1(10);A a2(10);A a3 = a1 + a2;a3.print();return 0;
}

上述是一个为类型A定义运算符+,+=,==,!=的实例。我们采用类成员定义复合赋值。
下述是一个复合赋值采用非成员的版本。

#include <iostream>
class A
{
friend bool operator==(const A &a1, const A &a2);
friend A operator+=(A &a1, const A &a2);
public:A():m_a(0), m_b(0){}A(int i):m_a(i), m_b(i){}void print(){printf("a_%d,b_%d\n", m_a, m_b);}private:int m_a;int m_b;
};// 返回值是复合赋值作为表达式的结果
// 参数1是复合赋值左边操作数,此操作数会被复合赋值所影响。
A operator+=(A &a1, const A &a2)
{a1.m_a += a2.m_a;a1.m_b += a2.m_b;return a1;
}A operator+(const A &a1, const A &a2)
{A sum = a1;sum += a2;return sum;
}bool operator==(const A &a1, const A &a2)
{return a1.m_a == a2.m_a && a1.m_b == a2.m_b;
}bool operator!=(const A& lhs, const A& rhs)
{return !(lhs == rhs);
}int main()
{A a1(10);A a2(10);A a3 = a1 + a2;a3.print();return 0;
}

4.4.关系运算符
4.4.1.最佳实践
(1).通常,关系运算符定义为非成员函数以允许左侧或右侧的运算对象进行转换。
(2).这些运算符一般不需改变运算对象的状态,故形参可以是常量的引用。
以下是一个关系运算符重载的例子

#include <iostream>class A
{
friend bool operator==(const A& a1, const A& a2);
public:A():m_a(0),m_b(0){}A(int i):m_a(i),m_b(i){}
private:int m_a;int m_b;
};bool operator==(const A& a1, const A& a2)
{if(a1.m_a == a2.m_a && a1.m_b == a2.m_b){return true;}else{return false;}
}bool operator!=(const A& a1, const A& a2)
{return !(a1 == a2);
}int main()
{A a1(1), a2(2);printf("a1==a2?%d\n", a1 == a2);return 0;
}

4.5.赋值运算符
(1).赋值运算符限制性因素有:
赋值作为表达式存在一个结果数值。
赋值会修改左边操作数状态。
(2).最佳实践
a,赋值一般实现为成员函数
b.为类型自定义赋值运算符时,运算符内部必须处理:
b.1.基类部分赋值
b.2.类型成员赋值
c.为类型自定义赋值运算符时,需要考虑自赋值场景。
d.赋值与拷贝针对包含资源的类涉及浅拷贝,深拷贝的概念。又涉及到左值,右值,移动语义,完美转发的概念。这些后续统一放在专门一篇讲解。
(3).一个实例

#include <iostream>class Norm1
{
public:Norm1(char i):m_i(i){}void print(){printf("norm1:%c\n", m_i);}
private:char m_i;
};class Norm2
{
public:Norm2(char i):m_i(i){}void print(){printf("norm2:%c\n", m_i);}
private:char m_i;
};class Base
{
public:Base(char c = 'b'):m_n1(c),m_n2(c){}void print(){printf("base:\n");m_n1.print();m_n2.print();}
private:Norm1 m_n1;Norm2 m_n2;
};class A : public Base
{
public:A(char c = 'a'):Base(c), m_n1(c+1),m_n2(c+1){}A& operator=(const A& a){if(this == &a){return *this;}Base::operator=(a);m_n1 = a.m_n1;m_n2 = a.m_n2;return *this;}void print(){Base::print();m_n1.print();m_n2.print();}
private:Norm1 m_n1;Norm2 m_n2;
};int main()
{A a1('x');A a2('a');a1.print();a2.print();a1 = a2;printf("after:\n");a1.print();a2.print();return 0;
}

4.6.下标运算符
对下标运算符最好定义常量和非常量版本,对常量版本返回常量引用。
4.6.1.强制性约束
从强制性约束角度要求形参列表只能有一个形参

#include <iostream>
class Norm
{
public:void print() const{printf("Norm_%d\n", m_i);}private:int m_i;
};class A
{
public:int operator[](Norm n){printf("test[]\n");return 1;}private:Norm m_arrA[10];
};int main()
{A a;Norm n;int i = a[n];return 0;
}

上述是一个合法的重载的[]运算符。
4.6.2.最佳实践
(1).形参最好是std::size_t。
(2).重载运算符最好提供常量版本,非常量版本。
这样对类型的常量实例,非常量实例均可执行[]运算。
(3).返回值应该设计成引用类型。
标准库类型[]运算符也均返回引用类型。

#include <iostream>
class Norm
{
public:void print() const{printf("Norm_%d\n", m_i);}private:int m_i;
};class A
{
public:Norm &operator[](std::size_t i){printf("[]\n");return m_arrA[i];}const Norm &operator[](std::size_t i) const{printf("[]const\n");return m_arrA[i];}private:Norm m_arrA[10];
};int main()
{A a;a[1].print();const A a1 = A();a1[1].print();return 0;
}

上述是一个[]运算符重载的最佳实例

4.7.递增和递减运算符
4.7.1.强制性约束
(1).对后置版本的++,--,形参列表必须提供单个类型为int的形参。
(2).对前置版本的++,--,形参列表必须为空。

#include <iostream>class A
{
public:A() : m_i(0) {}int operator++(){printf("++()\n");m_i++;return 1;}int operator++(int n){printf("++(int)\n");A temp = *this;m_i++;return 1;}private:int m_i;
};int main()
{A a;a++;++a;return 0;
}

上述是一个合法的前置++,后置++运算符的重载实现。

4.7.2.最佳实践
(1).定义++,--运算符时候,应该同时定义前置版本,后置版本。
(2).前置版本应该返回类型自身的引用。后置版本应该返回类型自身。
与标准库类型保持一致。
(3).不应该提供常量版本。

#include <iostream>
class Norm
{
private:int m_i;
};class A
{
public:A() : m_i(0) {}A &operator++(){printf("++()\n");m_i++;return *this;}A operator++(int){printf("++(int)\n");A temp = *this;m_i++;return temp;}private:int m_i;
};int main()
{A a;a++;++a;return 0;
}

上述是一个前置++,后置++的最佳实践。

4.8.成员访问运算符:*,->
4.8.1.强制性约束
(1).对*运算符,必须提供0个形参或1个形参。
(2).对->运算符,必须提供0个形参。

#include <iostream>class Norm
{
public:Norm(int i) : m_i(i) {}void print(){printf("i_%d\n", m_i);}private:int m_i;
};class A
{
public:int operator*(Norm){printf("*\n");return 1;}int operator->(){printf("->\n");return 2;}private:int m_i;
};int main()
{A a;A *p = &a;Norm n(1);p->operator->();p->operator*(n);a.operator*(n);a.operator->();// a->print();//*a;return 0;
}

上述我们为类型定义了一个合法的*运算符,一个合法的->运算符。

4.8.2.最佳实践
(1).*运算符的形参应该是0个。
因为只有这样,才能使用A a; *a;这样的方式触发*运算符访问。
(2).->运算符的返回类型,应该要么是重载了->运算符的类型,要么是指针类型。
因为如下述,我们执行A a; a->print();这样的操作时。操作处理细节为:
1.若a重载了->运算符。则执行运算符,获得返回值。否则,报错。
2.若返回值是指针类型,进入到对指针类型执行->情形。终止。
3.若返回值不是指针类型且重载了->运算符,则执行运算符,获得返回值。否则,报错。
4.进入2。

我们下述的a->print()对应的输出为:
在这里插入图片描述

#include <iostream>class Norm
{
public:Norm(int i) : m_i(i) {}Norm *operator->(){printf("norm ->\n");return this;}void print(){printf("i_%d\n", m_i);}private:int m_i;
};
class A
{
public:Norm operator*(){Norm n(1);printf("*\n");return n;}Norm operator->(){Norm n(11);printf("->\n");return n;}private:int m_i;
};int main()
{A a;A *p = &a;p->operator->();p->operator*();a.operator*();a.operator->();a->print();*a;return 0;
}

在这里插入图片描述
4.8.3.注意点
以4.8.2中实例为说明对象。整理c++类型中成员访问细节。

int main()
{A a;A *p = &a;p->operator->();p->operator*();a.operator*();a.operator->();a->print();*a;return 0;
}

(1).针对指针类型,基于指针执行->可以访问到指针指向对象的成员。此行为不允许重载。
(2).针对非指针类型,称其为对象类型,基于对象类型执行.可以访问到对象的成员。此行为不允许重载。
(3).调用类型*,->运算符的形式有显式和隐式。
a.显式调用诸如:

int main()
{A a;A *p = &a;p->operator->();p->operator*();a.operator*();a.operator->();return 0;
}

b.隐式调用诸如:

int main()
{A a;a->print();*a;return 0;
}

4.9.函数调用运算符
4.9.1.强制性约束
对形参列表无强制性约束。

#include <iostream>class A
{
public:A() {}int operator()(int){printf("(int)\n");return 0;}int operator()(int, int){printf("(int,int)\n");return 1;}private:int m_i;
};int main()
{A a;a(1);a(1, 2);return 0;
}

上述我们定义了两个函数调用运算符,分别接收一个参数,两个参数。

定义类函数调用运算符的类,类的对象称作函数对象。

4.10.转换构造函数
4.10.1.强制性约束
我们称只传递一个实参即可调用的构造函数为转换构造函数

#include <iostream>class A
{
public:A(int i, int j = 0) : m_i(i){printf("A(int)_%d\n", i);}private:int m_i;
};void fun(A a)
{
}int main()
{fun(1);return 0;
}

上述我们定义了一个转换构造函数。因为有了此转换构造函数,上述fun(1)执行中先基于1构造得到类型A的实例,再执行函数调用。
若要阻止这类默认转换,只需将转换构造用explicit修饰。此时,只能通过显式构造基于int得到类型A实例。

#include <iostream>class A
{
public:explicit A(int i, int j = 0) : m_i(i){printf("A(int)_%d\n", i);}private:int m_i;
};void fun(A a)
{
}int main()
{fun(A(1));return 0;
}

4.11.类型转换运算符
4.11.1.强制性约束
类型转换运算符的形参列表必须为空

#include <iostream>class Norm
{
public:
private:char m_c;
};class A
{
public:explicit A(int i, int j = 0) : m_i(i){printf("A(int)_%d\n", i);}operator int(){printf("int()\n");return m_i;}operator Norm(){printf("Norm()\n");Norm n;return n;}private:int m_i;
};void fun(A a)
{
}int main()
{fun(A(1));A a(1);int n = 1 + a;printf("n_%d\n", n);Norm nn = a;return 0;
}

上述我们定义了两个类型转换运算符,分别转换到int,Norm类型。

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

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

相关文章

2022年全国大学生数据分析大赛医药电商销售数据分析求解全过程论文及程序

2022年全国大学生数据分析大赛 医药电商销售数据分析 原题再现&#xff1a; 问题背景   20 世纪 90 年代是电子数据交换时代&#xff0c;中国电子商务开始起步并初见雏形&#xff0c;随后 Web 技术爆炸式成长使电子商务处于蓬勃发展阶段&#xff0c;目前互联网信息碎片化以…

css实现正六边形嵌套圆心

要实现一个正六边形嵌套圆心&#xff0c;可以使用CSS的::before和::after伪元素以及border-radius属性。以下是具体的解析和代码&#xff1a; 使用::before和::after伪元素创建正六边形。设置正六边形的背景色。使用border-radius属性使正六边形的内角为60度。在正六边形内部创…

【matlab程序】matlab画子图的多种样式

【matlab程序】matlab画子图的多种样式

【漏洞复现】速达软件存在任意文件上传

漏洞描述 速达软件全系产品存在任意文件上传漏洞,未经身份认证的攻击者可以通过此漏洞上传恶意后门文件,执行任意指令,造成服务器失陷 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、…

fastadmin列表头部按钮批量上传视频

上传界面通过layui生成 index.html <a href="{:url(video/piliangadd)}" class="btn btn-success btn-piliangadd btn-dialog {:$auth->check(video/piliangadd)?:hide}" title="批量上传" ><i class="fa fa-plus">…

flask之邮件发送

一、安装Flask-Mail扩展 pip install Flask-Mail二、配置Flask-Mail 格式&#xff1a;app.config[参数]值 三、实现方法 3.1、Mail类 常用类方法 3.2、Message类&#xff0c;它封装了一封电子邮件。构造函数参数如下&#xff1a; flask-mail.Message(subject, recipient…

【产品经理】业务问题的诊断:由简入繁,渐进成长

产品的价值在于解决了用户的问题。业务问题的诊断&#xff0c;发现用户的真实问题&#xff0c;拨云见日。 产品的价值在于解决了一定范围的问题&#xff0c;让整体的业务运转、参与角色都能收到因为产品的贡献而产生的价值或便利。微信解决了熟人之间的沟通问题&#xff0c;支付…

【React + Typescript】使用WebPack包管理、各种扩展插件组成的初始模板,开源协议:CC-BY-4.0

React Typescript Webpack 模板 模板展示项目结构使用的部分扩展包页面配置代码Layout 公共容器组件路由Jspackage.json 开源模板下载TIP 模板展示 项目结构 使用的部分扩展包 &#x1f4c2; System ├── &#x1f4c2; Plugin │ ├── &#x1f4c4; file-loader | 在处…

idea连接mysql详细讲解

IDEA连接mysql又报错&#xff01;Server returns invalid timezone. Go to Advanced tab and set serverTimezone prope 前进的道路充满荆棘。 错误界面 IDEA连接mysql&#xff0c;地址&#xff0c;用户名&#xff0c;密码&#xff0c;数据库名&#xff0c;全都配置好了&…

Mybatis异常org.apache.ibatis.binding.BindingException: Parameter “xxx“ not found

问题1: 可能是 mybatis 的xml&#xff0c;对应的mapper接口缺少Param注解&#xff0c;或者Param注解的value与xml的不一致 切记只要参数不是一个集合类型向下图或者多个参数值就要加Param注解 问题2: mybatis的xml&#xff0c;存在多余的注释。注释中包含#{}、${}。注释掉的代…

行云海CMS SQL注入漏洞复现

0x01 产品简介 行云海cms是完全开源的一套CMS内容管理系统,简洁,易用,安全,稳定,免费。 0x02 漏洞概述 行云海cms中ThinkPHP在处理order by排序时可利用key构造SQL语句进行注入,LtController.class.php中发现传入了orderby未进行过滤导致sql注入。攻击者除了可以利用 SQL 注入…

数字艺术语言processing初步

文章目录 示例程序刷新逻辑二维对象 Processing是专门用于艺术设计的编程语言&#xff0c;可以实现非常炫酷的图形和功能&#xff0c;比如下面这张联合国用的图就是用Processing编写的&#xff0c;本文简要介绍以下Processing&#xff0c;并列出其二维对象。 示例程序 第一步&…

删除PPT文件的备注内容

解决方案的工作经常汇报以及经常做ppt的回报工作&#xff0c;但是删除备注很痛苦。 在网上或者拿历史的ppt文件修改后&#xff0c;需要删除ppt备注内容以及删除ppt个人文件信息的办法&#xff1a; 现象&#xff1a;很多备注信息&#xff0c;需要删除 解决办法一、 文件--信息-…

Linux Docker 图形化工具 Portainer远程访问

文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 结束语 前言 Portainer是一个轻量级的容器管理工具&#xff0c;可以通过Web界面对Docker容器进行管理和监控。它提供了…

nodejs微信小程序+python+PHP就业求职招聘信息平台的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

ffmpeg编译支持AVS3编解码

libuavs3d ffmpeg的官方源码中已经支持了libuavs3d解码器的接口&#xff08;libavcodec/libuavs3d.c中定义&#xff09;&#xff0c;因此如果需要编译ffmpeg支持libuavs3d解码器&#xff0c;只需要安装libuavs3d.so以及开启ffmpeg的编译选项即可。 安装libuavs3d解码器 #代码仓…

小程序开发

1.准备工作 1.注册公众平台账号&#xff08;免费&#xff09;2.下载小程序开发工具&#xff08;免费&#xff09; 2.开发步骤 1.第一个小程序 1.小程序的AppID相当于小程序平台的一个身份证&#xff0c;这里使用的是测试号&#xff08;注意区别于服务号或订阅号的AppID&#xf…

学习前端都需要学什么?

前端开发是一门需要掌握多种技术和工具的综合性学科。作为一名合格的前端开发者&#xff0c;需要具备以下几方面的知识和技能&#xff1a; HTML&#xff1a;HTML 是构建网页的基础&#xff0c;是前端开发的第一步。需要掌握各种 HTML 标签的使用和语义化的编写方式&#xff0c…

本地快速部署Apache服务器并使用内网穿透实现远程访问

Apache服务安装配置与结合内网穿透实现公网访问 文章目录 Apache服务安装配置与结合内网穿透实现公网访问前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpo…

【自习室预约系统源码】基于springboot框架的自习室管理和预约系统设计

&#x1f345; 简介&#xff1a;500精品计算机源码学习 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 文末获取源码 目录 一、以下学习内容欢迎领取&#xff1a; 二、文档资料截图&#xff1a; 三想了解更多&#xff0c;请收藏、评论、留言&#xff1a;…