运算符重载使得用户自定义的数据以一种更简洁的方式工作

运算符重载规则

重载运算符的限制

可以重载的运算符

+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ' ->
[] () new delete new[] delete[]

不能重载的算符

. :: .* ?: sizeof

重载运算符函数可以对运算符作出新的解释,但原有基本语义不变

  1. 不改变运算符的优先级
  2. 不改变运算符的结合性
  3. 不改变运算符所需要的操作数
  4. 不能创建新的运算符

运算符重载的语法形式

运算符函数是一种特殊的成员函数或友员函数

成员函数的语法形式为

返回值类型 类名 :: operator op( 参数表 )
{
// 相对于该类定义的操作
}

一个运算符被重载后,原有意义没有失去,只是定义了相对一特定类的一个新运算符

用成员或友员函数重载运算符

运算符函数可以重载为成员函数或友员函数

关键区别在于成员函数具有 this 指针,友员函数没有this指针

不管是成员函数还是友员函数重载,运算符的使用方法相同

但传递参数的方式不同,实现代码不同,应用场合也不同

一元运算符

Object op或op Object

  • 重载为成员函数,解释为
Object.operator op()

操作数由对象Object通过this指针隐含传递

  • 重载为友员函数,解释为
operator op (Object)

操作数由参数表的参数Object提供

二元运算符

ObjectL op ObjectR

  • 重载为成员函数,解释为:
ObjectL.operator op( ObjectR )

左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递

  • 重载为友员函数,解释为:
operator op( ObjectL, ObjectR )

左右操作数都由参数传递

用成员函数重载

当一元运算符的操作数,或者二元运算符的左操作数是类的对象时,定义重载算符函数为成员函数

用友员函数重载

友员函数重载运算符常用于运算符的左右操作数类型不同的情况

在第一个参数需要隐式转换的情形下,使用友员函数重载运算符是正确的选择

友员函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换

C++中不能用友员函数重载的运算符有 =, (), [], ->

友元函数重载存在的问题

用友员函数重载像“++”这样的运算符时,有时会碰到问题

TriCoor TriCoor::operator++()
{
x++; y++; z++;
return *this;
}//ok,修改了this指针所指对象

用成员函数重载一元运算符时,所需要的唯一变元通过 this 指针传递,对 this 所指对象数据的任何改变都会影响到激活运算符函数的对象

  1. 若定义友员函数 friend operator++()版本:
TriCoor operator++(TriCoor opl)
{
opl.x++;
opl.y++;
opl.z++;
return opl;
}

函数使用传值参数,对 opl 的所有修改都无法传到函数体外,不会影响被调用的对象

2. 用指向激活对象的指针定义友员函数:

TriCoor operator ++ (TriCoor * opl)
{
opl->x++;
opl->y++;
opl->z++;
return *opl;
}

C++不知道如何激活该函数,下述代码无法编译:

TriCoor ob(1, 2, 3);
&ob++;//error
  1. 使用引用参数:
TriCoor operator ++ (TriCoor & opl)
{
opl.x++;
opl.y++;
opl.z++;
return opl;
}

下述代码是正确的:

TriCoor ob (1, 2, 3);
ob++;//ok,传名

如果一个运算符的操作要修改类的对象的状态,要重载为友员函数时,应该使用引用参数

若一运算符的操作需要修改类对象状态时,应该用成员函数重载;

需要左值操作数的运算符(如 ++,–),若重载为友员函数时要用引用参数

C++不能用友员重载的运算符:= () [] ->

如果运算符的操作数(尤其是第一个操作数)希望有隐式转换,则必须用友员函数重载

几个典型运算符重载

数学类中常用的几个运算符重载的特点和应用

重载++与–

A Aobject;

运算符 ++ 和 – 有两种方式:

  • 前置方式:++Aobject与–Aobject

    一元成员函数重载 A::A operator++ ();,解释为:Aobject.operator ++();

    友员函数重载 friend A operator++ (A &);,解释为:operator ++(Aobject);
  • 后置方式:Aobject++与Aobject–

    二元成员函数重载 A::A operator++ (int);,解释为:Aobject.operator ++(0);

    友员函数重载:friend A operator++ (A &, int);,解释为:operator++(Aobject, 0);

前置++重载

Complex& operator++()
{
this->a++;
this->b++;
return *this;
}

后置++重载

Complex operator++(int)
{
Complex tmp = *this;
this->a++;
this->b++;
return tmp;
}

重载赋值运算符

赋值运算符重载用于对象数据的复制

operator= 必须重载为成员函数

重载函数原型为:类型 & 类名::operator= (const 类名 &);

重载运算符[]和()

运算符 []() 是二元运算符

[]() 只能用成员函数重载,不能用友元函数重载

  • 重载下标运算符 []

    [] 运算符用于访问数据对象的元素

    重载格式:类型 类::operator[] (类型);

    e.g.

    x 是类 X 的一个对象,则表达式x[y]可被解释为x.operator[](y)
#include <iostream>
class vector
{
public:
vector(int n)
{
v = new int[n];
size = n;
}
~vector()
{
delete[] v;
size = 0;
}
int & operator[](int i)
{
return v[i];
}
private :
int *v;
int size;
};
void main()
{
vector a(5);
a[2] = 12;
cout << a[2] << endl;
}
  • 重载函数调用符 ()

    () 运算符用于函数调用

    重载格式:类型 类::operator()(表达式表);

    x 是类 X 的一个对象,则表达式x(arg1, arg2, … )可被解释为x.operator()(arg1, arg2, … )
#include <iostream>
class F
{
public:
double operator()(double x , double y);
};
double F::operator()(double x,double y)
{
return x * x + y * y;
}
void main()
{
F f;
cout << f(5.2, 2.5) << endl;
}

重载流插入和流提取运算符

istreamostream 是 C++ 的预定义流类

cinistream 的对象,coutostream 的对象

运算符 <<ostream 重载为插入操作,用于输出基本类型数据

运算符 >>istream 重载为提取操作,用于输入基本类型数据

用友员函数重载 <<>> ,输出和输入用户自定义的数据类型

friend ostream& operator<<(ostream &out, Complex &c1);
···
ostream& operator<<(ostream &out, Complex &c)
{
out<<c.a<<" + "<<c.b<<"i"<<endl;
return out;
}

重载运算符[],=,==,!=项目演示

Array.h

#ifndef _ARRAY_H_
#define _ARRAY_H_ class Array
{
private:
int mLength;
int* mSpace; public:
Array(int length);
Array(const Array& obj);
int length();
void setData(int index, int value);
int getData(int index);
~Array(); public:
int& Array::operator[](int i);
Array& Array::operator=(Array &a1);
bool operator==(Array &a2);
bool operator!=(Array &a2);
}; #endif

Array.cpp

#include "iostream"
#include "Array.h"
using namespace std; Array::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new int[mLength];
} Array::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
} int Array::length()
{
return mLength;
} void Array::setData(int index, int value)
{
mSpace[index] = value;
} int Array::getData(int index)
{
return mSpace[index];
} Array::~Array()
{
mLength = -1;
delete[] mSpace;
} //以下是运算符重载函数
//[]重载
int& Array::operator[](int i)
{
return mSpace[i];
} //=重载
Array& Array::operator=(Array &a)
{
int i = 0;
if (this->mSpace != NULL)
{
delete[] mSpace;
this->mLength = 0;
}
this->mLength = a.mLength;
this->mSpace = new int[a.mLength];
for (i=0; i<this->mLength; i++)
{
mSpace[i] = a[i];
}
return *this;
} //==重载
bool Array::operator==(Array &a2)
{
if (this->mLength != a2.mLength)
{
return false;
}
for (int i=0; i<this->mLength; i++)
{
if (this->mSpace[i] != a2[i])
{
return false;
}
}
return true;
} //!=重载
bool Array::operator!=(Array &a2)
{
return !(*this == a2);
}

ArrayTeat.cpp

#include "iostream"
#include "Array.h"
using namespace std; int main()
{
Array a1(10);
for(int i=0; i<a1.length(); i++)
{
//a1.setData(i, i);
a1[i] = i;
}
for(int i=0; i<a1.length(); i++)
{
//printf("array %d: %d\n", i, a1.getData(i));
printf("array %d: %d\n", i, a1[i]);
}
Array a2 = a1;
for(int i=0; i<a2.length(); i++)
{
//printf("array %d: %d\n", i, a2.getData(i));
printf("array %d: %d\n", i, a2[i]);
}
Array a3(20);
a2 = a3 = a1;
if (a1 == a2)
{
printf("相等\n");
}
else
{
printf("不相等\n");
}
if (a1 != a2)
{
printf("不相等\n");
}
else
{
printf("相等\n");
} system("pause");
return 0;
}

类类型转换

数据类型转换在程序编译时或在程序运行实现

基本类型 ←→ 基本类型

基本类型 ←→ 类类型

类类型 ←→ 类类型

类对象的类型转换可由两种方式说明:

构造函数

转换函数

称为用户定义的类型转换或类类型转换,有隐式调用和显式调用方式

构造函数进行类类型转换

当类 ClassX 具有以下形式的构造函数:

ClassX::ClassX(arg, arg1 = E1, ..., argn = En);

说明了一种从参数 arg 的类型到该类类型的转换

e.g.

class X
{
public:
X(int);
X(const char *, int = 0);
};
void f(X arg);
···
X a = X(1);// a = 1
X b = "Jessie";// b = X ( "Jessie" , 0 )
a = 2;// a = X ( 2 )
f(3);// f ( X ( 3 ) )
f(10, 20);// error

类型转换函数

带参数的构造函数不能把一个类类型转换成基本类型

类类型转换函数是一种特殊的成员函数,提供类对象之间显式类型转换的机制

语法形式:

X::operator T()
{
···
return T 类型的对象
}

功能:将类型 X 的对象转换为类型 T 的对象

  • T 可以是预定义类型,也可以是用户定义类型
  • 函数没有参数,没有返回类型,但必须有一条 return 语句,返回 T 类型的对象
  • 该函数只能为成员函数,不能为友员
class X
{
···
public:
operator int();
···
};
void f(X a)
{
int i = int(a);
i = (int)a;
i = a;
}

类型转换函数有两种使用方式:

  • 隐式使用 i = a;
  • 显式使用 i = a.operator int();//int(a) (int)a

    使用不同函数作类型转换函数:
    int i = a;//用类型转换函数进行转换
X i = a;//用构造函数进行转换

运算符重载运用

全局函数,类成员函数实现运算符重载

  1. 承认操作符重载是一个函数,写出函数名称
  2. 根据操作数写出函数参数
  3. 根据业务,完善函数返回值,实现函数业务

全局函数:写出函数实现,在类中添加友元函数声明,一般只有拿不到源代码的类参与才会用全局函数实现

操作符重载的方法

  • 全局函数友元函数法(技术推演)

    private声明使得类的成员不能被外界访问

    但是通过friend关键字可以例外的开放权限
  • 通过成员函数进行操作符的重载

    用成员函数重载的操作符,左操作数,通过this指针隐含传送

    比全局函数友元函数法少一个参数,即左操作数;不需要使用friend关键字
Complex c3 = c1 + c2;
Complex c3 = c1.operator+(c2);
  • 什么时候用全局函数重载操作符PK什么时候用成员函数重载操作符
  1. 当无法修改左操作数的类时,使用全局函数进行重载
  2. =, [], ()和->操作符只能通过成员函数进行重载

运算符重载总结

  • 运算符重载可以像基本数据类型一样,用简洁明确的运算符操作自定义的类对象。
  • 重载运算符函数可以对运算符作出新的解释,但原有基本语义不变。
  • 运算符函数既可以重载为成员函数,也可以重载为友员函数或普通函数。
  • 当一元运算符的操作数,或者二元运算符的左操作数是类的一个对象时,以成员函数重载;当一个运算符的操作需要修改类对象- 状态时,应该以成员函数重载。如果以成友员函数重载,则使用引用参数修改对象。
  • 当运算符的操作数(尤其是第一个操作数)希望有隐式转换,则重载算符时必须用友员函数。
  • 构造函数和类型转换函数可以实现基本类型与类类型,以及类类型之间的类型转换

C++学习笔记-运算符重载的更多相关文章

  1. C++学习之运算符重载的总结

    C++学习之运算符重载的总结              运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据导致不同行为的发生,C++为运算符重载提供了一种方法,即运算符重载函数 ...

  2. Dart学习笔记-运算符-条件表达式-类型转换

    Dart学习笔记-运算符-条件表达式-类型转换 一.运算符 1.算术运算符 + (加)- (减)* (乘)/ (除)~/ (取整) %(取余) 2.关系运算符 == (等等) != (不等) > ...

  3. 我的c++学习(8)运算符重载和友元

    运算符的重载,实际是一种特殊的函数重载,必须定义一个函数,并告诉C++编译器,当遇到该运算符时就调用此函数来行使运算符功能.这个函数叫做运算符重载函数(常为类的成员函数). 方法与解释 ◆ 1.定义运 ...

  4. C++学习26 运算符重载的概念和语法

    所谓重载,就是赋予新的含义.函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作.运算符重载(Operator Overloading)也是一个道 ...

  5. C++ Primer 学习笔记_63_重载运算符和转换 --转换和类类型【上】

    重载运算符和转换 --转换与类类型[上] 引言: 在前面我们提到过:能够用一个实參调用的位 unsignedchar 相同范围的值,即:0到255. 这个类能够捕获下溢和上溢错误,因此使用起来比内置u ...

  6. C++学习笔记(5)----重载自增自减运算符

    自增运算符“++”和自减运算符“--”分别包含两个版本.即运算符前置形式(如 ++x)和运算符后置形式(如 x++),这两者进行的操作是不一样的.因此,当我们在对这两个运算符进行重载时,就必须区分前置 ...

  7. STL学习之运算符(<<)重载问题和仿函数的实现

    /*   运算符<<的重载一直报错,   友原函数中可以访问到类的私有成员*/#include<iostream>using namespace std; class MyIn ...

  8. 高放的c++学习笔记之重载运算与类型转换

    ▲基本概念 (1)重载运算符是具有特殊名字的函数,它们的名字又operator和其后要定义的运算符号共同构成.. (2)对于一个运算符号来说它或者是类的成员,或者至少含有一个类类型的参数. (3)我们 ...

  9. VBA 学习笔记 - 运算符

    学习资料:https://www.yiibai.com/vba/vba_operators.html 算术运算符 加减乘除模指,这个没啥特别的. 比较运算符 和 Lua 相比,判断相等变成了一个等于号 ...

随机推荐

  1. redis主从配置及其java的调用(转)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/gsying1474/article/de ...

  2. 2g 大文件上传

    核心原理: 该项目核心就是文件分块上传.前后端要高度配合,需要双方约定好一些数据,才能完成大文件分块,我们在项目中要重点解决的以下问题. * 如何分片: * 如何合成一个文件: * 中断了从哪个分片开 ...

  3. pymysql pymysql.err.OperationalError 1045 Access denied最简单解决办法

    我使用的是python3.6+pymysql+mysql8.0 在cmd命令行直接输入mysql回车出现:ERROR 1045 (28000): Access denied for user 'ODB ...

  4. AtCoder AGC014E Blue and Red Tree (启发式合并)

    题目链接 https://atcoder.jp/contests/agc014/tasks/agc014_e 题解 完了考场上树剖做法都没想到是不是可以退役了... 首先有一个巨难写的据说是\(O(n ...

  5. 浅淡数据仓库(二)星型模式与OLAP多维数据库

    在关系数据库管理系统中实现的维度模型称为星型模型模式,因为其结构类似星型结构.在多为数据库环境中实现的维度模型通常称为联机分析处理(OLAP)多维数据库

  6. Rust格式化输出

    打印操作由 https://doc.rust-lang.org/std/fmt/ 里面所定义的一系列宏来处理,包括: format!:将格式化文本写到字符串(String).(译注:字符串是返 回值不 ...

  7. Backen-Development record 1

    单例模式 在应用这个模式时,单例对象的类必须保证只有一个实例存在. 服务进程中的其他对象再通过这个单例对象获取这些配置信息.这种方式简化了在复杂环境下的配置管理. __new__实现 用装饰器实现单例 ...

  8. ccf 201409-3 字符串匹配(toupper,tolower)

     ccf 201409-3 字符串匹配(toupper,tolower) 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行.你的程序还需支持大小写敏感选项:当选项打开时,表示同一 ...

  9. VMware安装CentOS后无法无法识别网卡的解决方法

    请找到安装CentOS虚拟机的安装目录,找到.vmx后缀名的文件,点击鼠标右键选择用记事本打开,在文件的最后一行添加内容:ethernet0.virtualDev = "e1000" ...

  10. NodejS---require的机制

    假设Y是路径,X是文件名或目录名,当 Nodejs 遇到 require(Y+X) 时,按照下面的顺序处理: 1.如果 X 是核心模块(例如:require("http")) a. ...