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

运算符重载规则

重载运算符的限制

可以重载的运算符

+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ' ->
[] () 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. Flyway的简单介绍和使用(转)

    Flyway的简单介绍及使用 一.开发时管理数据库遇到的问题: 现在开发一般都是团队开发,这样就会出现项目同步的问题,代码同步可以通过SVN工具管理起来,那数据库同步怎么办呢?理想的情况下,在开发新项 ...

  2. python---win32gui、win32con、win32api:winAPI操作

    python操作winAPI 窗口操作: import sys from PyQt5.QtWidgets import QApplication, QWidget from lianxi import ...

  3. 题解 【HEOI2016】tree树

    题面 解析 其实这题可以考虑离线做法,用并查集解决. 因为仔细想,添加标记并不方便, 但如果用并查集记录下祖先, 再一一删除,就会方便很多. 先把每次操作记录下来, 同时记录下每个点被标记的次数(因为 ...

  4. http 异步 接收 回传 数据文字和文件流

    public void HttpListenerStar() { try { HttpListener httpListener = new HttpListener(); httpListener. ...

  5. BZOJ 4269: 再见Xor 线性基+贪心

    Description 给定N个数,你可以在这些数中任意选一些数出来,每个数可以选任意多次,试求出你能选出的数的异或和的最大值和严格次大值. Input 第一行一个正整数N. 接下来一行N个非负整数. ...

  6. Gulp error in WebStorm: Failed to list gulp tasks

    I have the same problem with webstorm after install a updated version of node. The solution for me i ...

  7. 【CUDA 基础】3.4 避免分支分化

    - title: [CUDA 基础]3.4 避免分支分化 categories: - CUDA - Freshman tags: - 规约问题 - 分支分化 toc: true date: 2018- ...

  8. fanout(Publish/Subscribe)发布/订阅

    引言 它是一种通过广播方式发送消息的路由器,所有和exchange建立的绑定关系的队列都会接收到消息 不处理路由键,只需要简单的将队列绑定到交换机上 fanout交换机转发消息是最快的,它不需要做路由 ...

  9. Ngrinder脚本开发各细节锦集(groovy)

    Ngrinder脚本开发各细节锦集(groovy) 1.生成随机字符串(import org.apache.commons.lang.RandomStringUtils) 数字:RandomStrin ...

  10. MySQL中获取天、周、月等数据

    MySQL中获取天.周.月等数据 1.今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 2.昨天 SELECT * FROM 表名 ...