C++程序设计——运算符重载(运算符重载的概念;运算符成员函数与友元函数;单目运算符重载;重载流运算符;双目运算符重载;赋值运算符重载)

news2025/1/31 20:49:57

目录

前言

一、运算符重的概念与意义

1.运算符重载

(1)函数重载 

(2)运算符重载 

2.运算符重载的意义 

(1)例子引入 

(2)意义

(3)运算符重载的限制 

二、运算符成员函数/非成员函数

1.两种运算符重载方式

2.二者差别

3. 如何选择?

4.单双目运算符重载

三、单目运算符重载 

1.单目运算符重载特点

2.示例

3.重载单目运算符时需要注意的地方 

四、重载流运算符

1.流运算符回顾

2.重载流运算符 

五、双目运算符重载 

1.双目运算符重载

2.示例,重载 Complex 类的 +、-、*、/ 运算

3.补充

六、赋值运算符重载 

1.赋值运算符的缺省语义

2.缺省复制操作的副作用(举例说明)

总结


前言

学习面向对象程序设计,当构造大量的类时,为了使运算的方便,C++允许定义多个同名函数来处理类似却不同的形式的操作。

一、运算符重的概念与意义

1.运算符重载

(1)函数重载 

  • C++中允许定义多个同名函数来实现类似操作
  • 重载函数的参数一般不同(参数个数、类型或顺序不同)
  • 构造函数重载

(2)运算符重载 

  • +、-、*、/运算符的重载
  • cout、cin运算符的重载

2.运算符重载的意义 

(1)例子引入 

考虑下面关于复数类 complex 的定义:

class Complex {
private:
          double  real;     double  image;    // 实部,虚部 
public:
         Complex();   
         Complex(double, double);
         virtual ~Complex();           
         Complex add(const Complex &) const;   //复数加
         Complex sub(const Complex &) const;   //复数减
         Complex mul(const Complex &) const;   //复数乘
         Complex div(const Complex &) const;    //复数除
     void input();     //复数输入
     void print();     //复数输出
};

实现和调用 :

Complex  Complex::add (const Complex & c) const {
      Complex d(this->real + c.real, this->image+c.image);
      return d;
};
int main() {
      Complex c1(3,5); 
      Complex c2(2,6);
      Complex c = c1.add(c2);      
      return 0;
};

 观察下面的数学运算表达式 

  • 自然数加法:3+5
  • 有理数加法:3.5+5/2
  • 实数加法:e + 2.7
  • 复数加法:(2+4i) + (5+6i)

从数学表达式到程序表示

  1. 运算符“+”可应用于 double 类型及其子类型。
  2. 能否将“+、-、*、/”等运算符用于自定义的数据类型(如 Complex)中?

(2)意义

​使用运算符重载

class Complex {
      double  real;     double  image;    // 实部,虚部 
public:
      Complex();   
      Complex(double, double);
      ~Complex();           
      Complex operator+(const Complex &) const;   //复数加
      Complex operator-(const Complex &) const;   //复数减
      Complex operator*(const Complex &) const;   //复数乘
      Complex operator/(const Complex &) const;    //复数除,等会不写了
      friend ostream& operator<< (ostream&, Complex &); 
      friend istream& operator>> (istream&, Complex &);             
};

双目运算符重载

Complex  Complex::operator+ (const Complex & c) {
      Complex d(this->real + c.real, this->image+c.image);
      return d;
};
Complex  Complex::operator- (const Complex & c) {
      Complex d(this->real - c.real, this->image-c.image);
      return d;
};
Complex  Complex::operator* (const Complex & c) {
      Complex d(this->real * c.real - this->image*c.image, 
	this->real * c.image + this->image * c.real);
      return d;
};

流运算符重载

ostream& operator<< (ostream& os, Complex & c) {
     os << c.real<<“+i*”<<c.image;
     return os;  
}
istream& operator>> (istream& is, Complex & c) {
     is >> c.real>>c.image;
     return is;  
}

调用程序

int main()
{
    Complex x, y, z;
    cin >> y >> z;
    x = y + z;
    cout << x << endl;
    cout << y – z << endl;
    return 0;
}

意义

  • 通过运算符能实现的功能通过函数一样能够实现,运算符重载不会在程序功能上带来好处
  • 使用运算符能使程序变得更加清晰、简单、符合人们的习惯
  • 运算符重载使C++具有更好的可扩充性,这也是C++最具吸引力的特点之一

(3)运算符重载的限制 

C++ 中可被重载的运算符

  • 算术运算:+、-、*、/、%、++、--
  • 位运算:^、&、| 、~、>>、<<
  • 逻辑/关系运算:!、&&、||、<、>、>=、<=、==、!=
  • 赋值运算:=、+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
  • 其他:->、,、[]、()、new、delete

不允许被重载的运算符

  • . 、::、?:、sizeof

对运算符重载的限制

  • 运算符重载不能改变运算符的优先级
  • 运算符重载不能改变运算符的结合性
  • 运算符重载不能使用默认参数
  • 运算符重载不能改变操作数的个数
  • 不能建立新的运算符
  • 运算符重载只能作用于自定义数据类型
  • 重载运算符 () 、[]、->、=时,必须使用成员函数进行重载
  • 所有运算符必须显式重载

二、运算符成员函数/非成员函数

1.两种运算符重载方式

  1. 通过成员函数进行重载
  2. 通过非成员(友元)函数进行重载

2.二者差别

  1. 通过成员函数重载的运算符,在调用时,其第一操作数必须是该类的一个对象
  2. 使用非成员函数重载的运算符无此限制,只需要调用时的参数类型与定义时依次相同即可

3. 如何选择?

  • 一般情况下,尽量使用成员函数进行重载
  • 某些特殊情况只能使用非成员函数,比如流操作和需要保留某些运算符的可操作性

4.单双目运算符重载

单目运算符重载(以“  !”为例) 

  1.  使用成员函数/方法进行重载:
  2.  使用非成员函数进行重载: 

双目运算符重载(以“ + ”为例) 

  1.  使用成员函数进行重载:
  2. 使用非成员函数进行重载:

三、单目运算符重载 

1.单目运算符重载特点

  • 采用成员方法/函数重载单目运算符时,参数数目为 0
  • 重载单目运算符时,其唯一的操作数只能是(自定义)对象或者其引用

2.示例

定义字符串类型 String,并以成员函数方式重载运算符 !,当字符串 s 长度为 0 时 !s 返回值为 true,否则 !s 返回值为 false。

class String {
public:
      String (const char* m = NULL); 
      ~String();
      bool operator! ();      
private:
      char* str;
};

String::String(const char* m) {
   if (m==NULL)  
	str=NULL;
   else {
	str = new char[strlen(m)+1];
	strcpy(str,m);
   }
}

String::~String() {
   if (str!=NULL) delete [] str;  
}

bool String::operator! () {
   if (str==NULL || strlen(str)==0)  
   	return  true;
   else 
	return false;
}

int main (int argc, char* argv[]) {
     String s1, s2(“Something”);
     if (!s1) cout<<“s1 is empty!”<<endl;
     if (!s2) cout<<“s2 is empty!”<<endl;   
    //这里,!s1 和 !s2 本质上是 s1.operator!() 和 s2.operator!() 的简写。
     return 0;
}

还可以以友元函数的方式进行重载 

class String {
      friend bool operator! (const String &);
public:
      String (const char* m = NULL); 
      ~String();      
private:
      char* str;
};

bool operator! (const String & str) {
   if (str.str==NULL || strlen(str.str)==0)  
   	return  true;
   else return false;
}

3.重载单目运算符时需要注意的地方 

自增运算符(++)和自减运算符(--)有两种使用方式,即前置方式与后置方式。C++ 规定,重载后置方式的自增/自减运算时,需要增加一个额外 int 型参数


四、重载流运算符

1.流运算符回顾

  • 流插入运算符 “<<”和流提取运算符“>>”,其第一操作数分别是一个 ostream 对象(典型的如 std::cout)和一个 istream 对象(如 std::cin)
  • cin 和 cout 对象支持任意基本数据类型的输入/输出操作。这是因为每个基本类型均在 istream / ostream 中有相应的重载(overload)

 若希望自定义类型支持标准输入/输出,应如何实现?如:

Complex  c(3,5);        // c 是一个复数对象
cout << c << endl;     // 通过标准输出打印 c  

2.重载流运算符 

  • 这时,应该重载 <<  运算符;显然不应该修改系统标准库中 ostream 的实现
  • 如果通过 Complex 类的成员函数重载 << 运算符,那么在使用该运算符时,必须是某个Complex 类的对象作为第一操作数,这显然不行
  • 因此,必须用非成员函数对其进行重载
ostream& operator<< (ostream& os, Complex & c) {
     os << c.real<<“+i*”<<c.image;
     return os;  
};

istream& operator>> (istream& is, Complex & c) {
     is >> c.real>>c.image;
     return is;  
};

五、双目运算符重载 

1.双目运算符重载

  • 回顾:前面讨论了两个特殊的双目运算符——流插入和流提取运算符的重载。
  • 分析:重载双目运算时需要考虑两个因素:一是被重载的运算符对左右操作数是否有要求;二是是否要保留运算符的可交换性。
  • 总结:双目运算被重载为带有一个参数的成员函数或者两个参数的非成员函数,且重载为非成员函数时必须有一个参数为类对象或者类对象的引用。

2.示例,重载 Complex 类的 +、-、*、/ 运算

class Complex {
      double  real;     double  image;    // 实部,虚部 
public:
      Complex();   Complex(double, double);
      virtual ~Complex();           
      double getReal();    double getImage(); 
      void  setReal(double);  void setImage(double);
      Complex operator+(const Complex &);   //复数加
      Complex operator-(const Complex &);   //复数减
      Complex operator*(const Complex &);   //复数乘
      Complex operator/(const Complex &);    //复数除
      friend ostream& operator<< (ostream&, Complex &); 
      friend istream& operator>> (istream&, Complex &);             
};
Complex  Complex::operator+ (const Complex & c) {
      Complex d(this->real + c.real, this->image+c.image);
      return d;
};

Complex  Complex::operator- (const Complex & c) {
      Complex d(this->real - c.real, this->image-c.image);
      return d;
};

Complex  Complex::operator* (const Complex & c) {
      Complex d(this->real * c.real - this->image*c.image, 
	this->real * c.image + this->image * c.real);
      return d;
};

3.补充

练习

  1. 重载 List 类的 [] 运算符,用以实现下标功能
  2. 思考,函数的原型应该是什么?

问题

何时应该返回引用?何时返回值?

——当整个表达式“具有左值属性时”,换句话说,能够被赋值时,应该返回引用。

Node& List::operator[] (int index) 
{
     for(Node* pnt=head; pnt != NULL && index>0; )
      {
	   pnt = pnt->next; 
	   index--;
     }    // 定位到链表的第 index 个节点
     if (pnt == NULL)  
	 throw  0;   // 抛出异常
    
     return (*pnt);  // 获得 pnt 节点处的 value 值                     
}

六、赋值运算符重载 

1.赋值运算符的缺省语义

事实上,赋值运算符不需显式重载便可直接使用在自定义类对象之间;其缺省操作是逐个复制所有的成员(浅拷贝)。比如                  

的执行结果是使 c1.real 和 c1.image 的值分别赋为 3 和5。但是,当对象成员中含有指针字段时,这样的操作会带来一定的副作用。

2.缺省复制操作的副作用(举例说明)

Stack s1(15);
Stack s2;
s2=s1;

上方程序会造成什么副作用?

该赋值发生后:

  • s1.data 和 s2.data 将指向相同的内存空间
  • s2 原来指向的内存空间丢失
  • 对 s1的操作也会影响到 s2,反之亦然
     

因此,在这种情况下,应该重载赋值运算符:

Stack& Stack::operator= (const Stack & st){
      if (&st!=this){          // important!!! 	
             delete[] data;
             data = new int [st.size];
             top = st.top;
             size=st.size;
             for(int i=0; i<top; i++)  // 数据复制
	      data[i] = st.data[i];
     }
     return * this;	         
}

总结

重载函数/运算符的操作实则有很多细节和限制,本篇博客总结了课堂的PPT以及补充内容,其中的真正知识运用还得实践操作才能融会贯通!

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

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

相关文章

有人问,普通人学python有意义吗?看看这位大佬怎么说

普通人学python有意义吗&#xff1f; 现在随着python越来越火&#xff0c;尤其是它成为了人工智能的第一编程语言&#xff0c;还被纳入了中小学的教育中。并且python的应用范围很广泛&#xff0c;可以解决很多专业或非专业的问题。 但python真的适合普通人学习吗&#xff1f;…

STAMP算法实战

1.案例知识点 推荐系统任务描述:通过用户的历史行为(比如浏览记录、购买记录等等)准确的预测出用户未来的行为;好的推荐系统不仅如此,而且能够拓展用户的视野,帮助他们发现可能感兴趣的却不容易发现的item;同时将埋没在长尾中的好商品推荐给可能感兴趣的用户。STAMP推荐…

普通二本,去过阿里外包,到现在年薪40W+的高级测试工程师,我的两年转行心酸经历...

我是一个普通二本大学机械专业毕业&#xff0c;17年毕业&#xff0c;19年转行&#xff0c;目前做IT行业的软件测试已经有3年多&#xff0c;职位是高级软件测试工程师&#xff0c;坐标上海… 我想现在我也有一点资格谈论关于转行这个话题&#xff1b;希望你在决定转行之前&…

【网络工程】如何本地调试微信公众号开发教程(Nginx代理方法)

目录 前言 目的 通过Nginx代理实现本地调试微信公众号 实现工具 实现步骤 1.启动本地前端项目 2.首先配置Nginx 3.填写app.conf内容&#xff0c;把本地前端项目与域名形成映射。 4.把app.conf加入到Nginx配置中 5.打开我们安装好的SwitchHosts工具 6.右键管理员权限…

全球电子烟行业快速发展,我国监管政策趋严行业面临重构

一、电子烟在全球范围广泛流行 根据观研报告网发布的《2022年中国电子烟行业分析报告-行业现状与发展趋势分析》显示&#xff0c;电子烟是在最近几年出现的一种电子产品&#xff0c;电子烟宣传的健康无害、有一定满足感对消费者有绝佳的吸引力;电子烟的品味、档次、个性化是吸…

零基础编程学习指南!让你不再迷茫~

一篇初学者干货&#xff0c;请耐心看完&#xff0c;希望对你有帮助。 作为初学者的你&#xff0c;命中了以下问题吗&#xff1f; #张三丰&#xff1a;编程是什么&#xff0c;怎么编程&#xff1f; #张无忌&#xff1a;what&#xff0c;比土木工程好&#xff1f; #成昆&…

Linux限制磁盘与内存配额【超详细】

大家好&#xff0c;我是早九晚十二&#xff0c;目前是做运维相关的工作。写博客是为了积累&#xff0c;希望大家一起进步&#xff01; 我的主页&#xff1a;早九晚十二 文章目录Linux限制磁盘用量的方式什么是磁盘配额磁盘配额的条件安装quota工具配额步骤新建一个磁盘分区新建…

NVMe 原理 - 命令的处理

蛋蛋读NVMe之一 (ssdfans.com) NVMe 所处层次 NVMe是一种Host与SSD之间通讯的协议&#xff0c;它在协议栈中隶属高层。NVMe在协议栈中处于应用层或者命令层。 NVMe是为SSD所生的。NVMe出现之前&#xff0c;SSD绝大多数走的是AHCI和SATA的协议&#xff0c;后者其实是为传统HDD…

元宇宙产业委与中国传媒大学就“虚拟空间文化生产与管理微专业”课程深度合作

12月17日、18日两天下午&#xff0c;由中国移动通信联合会元宇宙产业工作委员会牵头&#xff0c;为中国传媒大学虚拟空间文化生产与管理微专业成功邀请5位行业内大咖级讲师&#xff0c;为校内、外在读本科生和研究生&#xff0c;以及对虚拟文化生产与管理感兴趣且获得学士学位的…

工控CTF之协议分析6——s7comm

协议分析 流量分析 主要以工控流量和恶意流量为主&#xff0c;难度较低的题目主要考察Wireshark使用和找规律&#xff0c;难度较高的题目主要考察协议定义和特征 简单只能简单得干篇一律&#xff0c;难可以难得五花八门 常见的工控协议有&#xff1a;Modbus、MMS、IEC60870、…

windows下安装make,使用makefile文件

文章目录前言Makefile简介make作用安装make&#xff1a;1.windows上安装&#xff1a;chocolatey一、Chocolatey介绍二、Chocolatey安装安装make配置make连接前言 本人在学习go-micro中&#xff0c;用到Makefile&#xff0c;本人之前用过Makefile&#xff0c;但是不知道为什么这…

【蓝桥杯】第12届Scratch国赛中级组第1题 -- 文字特效

[导读]&#xff1a;蓝桥杯大赛是工业和信息化部人才交流中心举办的全国性专业信息技术赛事。蓝桥杯大赛首席专家倪光南院士说&#xff1a;“蓝桥杯以考促学&#xff0c;塑造了领跑全国的人才培养选拨模式&#xff0c;并获得了行业的深度认可。” 春雷课堂计划推出Scratch蓝桥杯…

红袖添香,绝代妖娆,Ruby语言基础入门教程之Ruby3基础语法,第一次亲密接触EP01

书接上回&#xff0c;前一篇我们在全平台构建好了Ruby3的开发环境&#xff0c;现在&#xff0c;可以和Ruby3第一次亲密接触了。 Ruby是一门在面向对象层面无所不用其极的解释型编程语言。 我们可以把编写Ruby代码看作是一场行为上的艺术&#xff0c;编码就像跳舞一样&#xf…

工控CTF之协议分析3——IEC60870

协议分析 流量分析 主要以工控流量和恶意流量为主&#xff0c;难度较低的题目主要考察Wireshark使用和找规律&#xff0c;难度较高的题目主要考察协议定义和特征 简单只能简单得干篇一律&#xff0c;难可以难得五花八门 常见的工控协议有&#xff1a;Modbus、MMS、IEC60870、…

关于要不要转行做程序员的问题

写在前边&#xff1a; 种一棵树&#xff0c;最好的时间是十年前&#xff0c;其次是现在。 什么时候开始都不晚。 这篇文章废话少说&#xff0c;我作为一个普通本科电气工程及其自动化专业的人转行作为Python程序员来说的经历到现在已经工作两年了&#xff0c;距离培训整整过…

LabVIEW使用硬件抽象层适应不同的接口

LabVIEW使用硬件抽象层适应不同的接口 在实验室工作时&#xff0c;拥有不同品牌/型号的各种设备&#xff0c;有时&#xff08;或多或少&#xff09;具有相同的目的。为了缩短开发时间&#xff0c;想创建一个硬件抽象层&#xff0c;允许使用所有相同类型的仪器&#xff0c;并具…

操作系统实验6:地址映射与共享

本次实践项目有两个基本内容&#xff1a; &#xff08;1&#xff09;用Bochs调试工具跟踪Linux-0.11的地址转换过程&#xff1b; &#xff08;2&#xff09;实现基于共享物理页框的进程间内存共享。 知识点补充 GDT和GDTR 和一个段有关的信息需要 8 个字节来描述&#xff0c…

【3D目标检测】Orthographic Feature Transform for Monocular 3D Object Detection

目录概述细节网络结构正交特征变换模块其余部分概述 本文是基于单目图像的3D目标检测方法。 【2018】【OFT-Net】 研究的问题&#xff1a; 在图像表示中&#xff0c;物体的比例会随着深度的变化而变化&#xff1b;物体的外观随着视点的不同而不同&#xff1b;物体的距离&…

看漫画也能学Python?小学生都能学会,《看漫画学python 2》PDF中文超清版,可分享

学习Python的小伙伴大部分应该都知道《看漫画学Python&#xff1a;有趣、有料、好玩、好用&#xff08;全彩版&#xff09;》这本书&#xff01; 《看漫画学Python》进阶版&#xff0c;在第1版的基础上讲解Python进阶知识&#xff0c;帮助读者完善Python知识体系&#xff0c;提…

点云Delaunay三角剖分(三维)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 四面体网格是一种三维非结构化网格,它划分了一个三维域。这种类型的分区有许多有利的性质。例如,它很适合具有任意复杂几何结构的领域,它可以很容易地在局部细化和粗化(没有悬挂节点),并且它可以完全自动地创建…