《C++程序设计基础教程》——刘厚泉,李政伟,二零一三年九月版,学习笔记
更多有趣的代码示例,可参考【Programming】
在C++中,运算符重载是一种允许程序员为用户定义的类型(如类和结构体)指定如何使用标准运算符(如 +, -, *, /, ==, <<, >>
等)的特性。通过运算符重载,可以使得自定义类型能够像内置类型一样参与运算,从而增强代码的可读性和易用性。
C++ 运算符重载实际上是函数重载
运算符重载的语法
函数类型 operator 运算符名称(形参列表)
{
运算符重载处理
}
operator
是 C++ 中专门用于定义重载运算符的关键字
eg 10-1 运算符重载举例
#include
using namespace std;
class RMB
{
private:
int yuan;
int fen;
public:
RMB(int y, int f);
RMB operator +(RMB &); // 重载+
RMB operator -(RMB &); // 重载-
void display()
{
cout << (yuan + fen/100.0) << endl;
}
};
RMB::RMB(int y, int f)
{
yuan = y;
fen = f;
}
RMB RMB::operator +(RMB & r) // 定义运算符函数
{
int ysum = yuan + r.yuan;
int fsum = fen + r.fen;
if(fsum>=100)
{
fsum -= 100;
ysum += 1;
}
RMB result(ysum, fsum); // 创建 RMB 对象 result
return result;
}
RMB RMB::operator -(RMB & r) // 定义运算符函数
{
int ysub = yuan - r.yuan;
int fsub = fen - r.fen;
if(fsub<0)
{
fsub += 100;
ysub -= 1;
}
RMB result(ysub, fsub); // 创建 RMB 对象 result
return result;
}
int main()
{
RMB r1(4,40);
RMB r2(2,50);
RMB r3(0,0);
RMB r4(0,0);
r3 = r1 + r2; // 调用重载运算符
r3.display();
r4 = r1 - r2; // 调用重载运算符
r4.display();
r4 = r2.operator+(r4);
r4.display();
return 0;
}
output
6.9
1.9
4.4
注意这里重载的时候,返回的对象要新建立,不然运算完后加数被加数或者减数被减数会被改变
r1 + r2 等价于 r1.operator+(r2)
,r1-r2 等价于 r1.operator-(r2)
C++中不是所有的运算符都可以重载,eg:
?:
.
::
.*
重载运算符时,不能改变运算符的优先级、结合性、操作数的数目,也不能改变运算符的语法语义
运算符重载有两种实现方式,分别为类的成员函数和友元函数
只能使用类的成员函数形式重载的运算符有:=
、()
、[]
、->
、new
、delete
单目运算符 ++
和 --
为了区别前置和后置,重载的时候有如下区别
前置自增++
<类型>operator++() // 作为类成员
<类型>operator++(<类型>) // 作为友元函数
后置自增++
<类型>operator++(int) // 作为类成员
<类型>operator++(<类型>, int) // 作为友元函数
区别就在于这个 int 类型的参数(通常命名为 dummy
,实际上这个参数在函数体内不会被使用,仅用于区分前置和后置自增运算符),dummy 的中文意思为笨蛋
调用方法
前置自增++
++a 或 a.operator++() // 作为类成员
operator++(a) // 作为友元函数
后置自增++
a++ 或 a.operator++(0)// 作为类成员
operator++(a, 0) // 作为友元函数
(1)单目运算符重载为类的成员函数
eg 10-2 单目运算符重载为类的成员函数举例,实现人民币分的自增
#include
using namespace std;
class RMB
{
private:
int yuan;
int fen;
public:
RMB(int y, int f)
{
yuan = y;
fen = f;
while(fen>=100)
{
fen -= 100;
yuan += 1;
}
}
RMB operator++(); // 重载前置自增
RMB operator++(int); // 重载后置自增
void display()
{
cout << (yuan + fen/100.0) << " 元" << endl;
}
};
RMB RMB::operator++()
{
fen += 1;
if(fen>=100)
{
fen -= 100;
yuan += 1;
}
RMB result(yuan, fen);
return result;
}
RMB RMB::operator++(int dummy)
{
RMB temp(yuan,fen); // 保存当前对象的副本
fen += 1;
if(fen>=100)
{
fen -= 100;
yuan += 1;
}
return temp;
}
int main()
{
RMB d1(1, 100);
RMB d2(0, 0);
++d1;
d1.display();
d2=d1++;
d2.display();
d1.display();
return 0;
}
output
2.01 元
2.01 元
2.02 元
注意后置重载的细节,先复制,再操作,然后把复制的返回
也可以用 *this
替代,this
是一个指向当前对象的指针,而 *this
则表示当前对象本身。使用 *this
可以在成员函数中直接访问当前对象的成员,就像访问一个普通对象一样。
前置如下
RMB RMB::operator++()
{
fen += 1;
if(fen>=100)
{
fen -= 100;
yuan += 1;
}
//RMB result(yuan, fen);
//return result;
return (*this);
}
后置如下
RMB RMB::operator++(int dummy)
{
//RMB temp(yuan,fen); // 保存当前对象的副本
RMB temp = *this;
fen += 1;
if(fen>=100)
{
fen -= 100;
yuan += 1;
}
return temp;
}
++d1 等价于 d1.operator++()
d1++ 等价于 d1.operator++(0)
int main()
{
RMB d1(1, 100);
RMB d2(0, 0);
d1.operator++();
d1.display();
d2=d1.operator++(0);
d2.display();
d1.display();
return 0;
}
(2)双目运算符重载为类的成员函数
eg 10-3 双目运算符重载为类的成员函数
#include
using namespace std;
class Ccomplex
{
private:
double real;
double imag;
public:
Ccomplex()
{
real = 0;
imag = 0;
}
Ccomplex(double r, double i)
{
real = r;
imag = i;
}
Ccomplex operator+(Ccomplex c)
{
double r = real + c.real;
double i = imag + c.imag;
Ccomplex result(r,i);
return result;
}
Ccomplex operator-(Ccomplex c)
{
double r = real - c.real;
double i = imag - c.imag;
Ccomplex result(r,i);
return result;
}
void display()
{
cout << "(" << real << "," << imag << "i)" << endl;
}
};
int main()
{
Ccomplex a(1,2), b(3,4), c, d;
c = a + b;
d = a - b;
cout << "c=";
c.display();
cout << "d=";
d.display();
return 0;
}
output
c=(4,6i)
d=(-2,-2i
c = a + b;
其实等价于 c = a.operator+(b)
。
d = a - b;
其实等价于 d = a.operator-(b);
可以写成如下形式
int main()
{
Ccomplex a(1,2), b(3,4), c, d;
//c = a + b;
c = a.operator+(b);
//d = a - b;
d = a.operator-(b);
cout << "c=";
c.display();
cout << "d=";
d.display();
return 0;
}
(1)单目运算符重载为友元函数
eg 10-4 单目运算符重载为友元函数举例
#include
using namespace std;
class RMB
{
private:
int yuan;
int fen;
public:
RMB(int y, int f)
{
yuan = y;
fen = f;
while(fen>=100)
{
fen -= 100;
yuan += 1;
}
}
friend RMB operator--(RMB &r); // 重载前置自减
friend RMB operator--(RMB &r, int); // 重载后置自减
void display()
{
cout << (yuan + fen/100.0) << " 元" << endl;
}
};
RMB operator--(RMB &r)
{
r.fen -= 1;
if(r.fen < 0)
{
r.fen += 100;
r.yuan -= 1;
}
return r;
}
RMB operator--(RMB &r, int dummy)
{
RMB temp(r.yuan, r.fen); // 保存当前对象的副本
r.fen -= 1;
if(r.fen<0)
{
r.fen += 100;
r.yuan -= 1;
}
return temp;
}
int main()
{
RMB d1(1, 100);
RMB d2(0, 0);
//--d1;
operator--(d1);
d1.display();
//d2=d1--;
d2=operator--(d1, 0);
d2.display();
d1.display();
return 0;
}
接着 10-2 的例子,自增换成自减重载,重载定义成友元函数后,友元函数可以访问私有变量
--d1;
等价于 operator--(d1);
d2=d1--;
等价于 d2=operator--(d1, 0);
(2)双目运算符重载为友元函数
eg 10-6 双目运算符重载为友元函数举例
#include
using namespace std;
class Ccomplex
{
private:
double real;
double imag;
public:
Ccomplex()
{
real = 0;
imag = 0;
}
Ccomplex(double r, double i)
{
real = r;
imag = i;
}
friend Ccomplex operator+(Ccomplex c1, Ccomplex c2);
friend Ccomplex operator-(Ccomplex c1, Ccomplex c2);
void display()
{
cout << "(" << real << "," << imag << "i)" << endl;
}
};
Ccomplex operator+(Ccomplex c1, Ccomplex c2)
{
double r = c1.real + c2.real;
double i = c1.imag + c2.imag;
Ccomplex result(r,i);
return result;
}
Ccomplex operator-(Ccomplex c1, Ccomplex c2)
{
double r = c1.real - c2.real;
double i = c1.imag - c2.imag;
Ccomplex result(r,i);
return result;
}
int main()
{
Ccomplex a(1,2), b(3,4), c, d, e;
//c = a + b;
c = operator+(a, b);
//d = a - b;
d = operator-(a, b);
cout << "c=";
c.display();
cout << "d=";
d.display();
return 0;
}
基于 eg 10-3 改改
output
c=(4,6i)
d=(-2,-2i)
c = a + b;
等价于 c = operator+(a, b);
d = a - b;
等价于 d = operator-(a, b);
eg 10-6 重载赋值运算符 “=”
#include
#include
using namespace std;
class String
{
private:
int len;
char *con;
public:
String() // 构造函数
{
cout << "String()..." << endl;
len = 0;
con = nullptr;
}
String(String &str) // 拷贝构造函数
{
cout << "String(&str)..." << endl;
len = str.len;
if (str.len)
{
con = new char[len + 1];
strcpy(con, str.con);
}
else
{
len = 0;
con = nullptr;
}
}
String(char *str) // 构造函数
{
cout << "String(char *)..." << endl;
if (str)
{
len = strlen(str);
con = new char[len + 1];
strcpy(con, str);
}
else
{
len = 0;
con = nullptr;
}
}
~String() // 析构函数
{
cout << "~String()...";
if (con)
{
cout << "con=" << con << endl;
delete[] con;
}
}
friend String operator+(String s1, String s2);
String operator=(String s)
{
cout << "operator=" << endl;
if(this == &s)
{
return *this;
}
else
{
len = s.len;
if (con)
delete[] con;
con = new char[len + 1];
strcpy(con, s.con);
return *this;
}
}
};
String operator+(String s1, String s2)
{
cout << "operator+" << endl;
String s;
s.len = s1.len + s2.len;
if (s.con)
delete[] s.con;
s.con = new char[s.len + 1];
strcpy(s.con, s1.con); // 会将源字符串的完整内容复制到目标字符串中,包括末尾的空字符 '\0'。
strcat(s.con, s2.con); // 会从目标字符串的末尾(即第一个 '\0' 处)开始追加源字符串。
return s;
}
int main()
{
String s1("123");
String s2("45678");
String s3;
s3 = s1 + s2;
return 0;
}
output
String(char *)...
String(char *)...
String()...
String(&str)...
String(&str)...
operator+
String()...
operator=
String(&str)...
~String()...con=12345678
~String()...con=12345678
~String()...con=123
~String()...con=45678
~String()...con=12345678
~String()...con=45678
~String()...con=123
按顺序解析下,String(char *)...
、String(char *)...
、String()...
创建对象 s1、s2、s3 时候调用构造函数
String(&str)...
和 String(&str)...
是传递 String operator+(String s1, String s2)
s1 和 s2 的形参调用拷贝构造函数,先调用 s2 的,再调用 s1 的
打印 operator+
,创建对象 String s;
调用 String()...
接下来 String operator=(String s)
中形参传递没有调用构造函数(是不是因为在一个表达式里面,作用域相同,前面 + 的 return 作用域还在没有释放,所以不用构造?)
打印 operator=
,return *this
的时候调用拷贝构造函数 String(&str)...
运算结束,开始析构
首先释放 *this
的 ~String()...con=12345678
再释放 operator+
中的临时变量 s
,~String()...con=12345678
接下来释放 operator+
的形参,s1 ~String()...con=123
和 s2 ~String()...con=45678
释放对象 s3,~String()...con=12345678
释放对象 s2,~String()...con=45678
释放对象 s1,~String()...con=123
仅调用
String s1("123");
String s2("45678");
String s3;
output
String(char *)...
String(char *)...
String()...
~String()...~String()...con=45678
~String()...con=123
s3 = s1 + s2;
其实等价于 s3.operator=(operator+(s1, s2));
int main()
{
String s1("123");
String s2("45678");
String s3;
//s3 = s1 + s2;
s3.operator=(operator+(s1, s2));
return 0;
}
out
String(char *)...
String(char *)...
String()...
String(&str)...
String(&str)...
operator+
String()...
operator=
String(&str)...
~String()...con=12345678
~String()...con=12345678
~String()...con=123
~String()...con=45678
~String()...con=12345678
~String()...con=45678
仅仅采用如下的形式,观察下构造和析构函数的调用顺序
int main()
{
String s1("123");
String s2("45678");
s1 = s2;
return 0;
}
output
String(char *)...
String(char *)...
String(&str)...
operator=
String(&str)...
~String()...con=45678
~String()...con=45678
~String()...con=45678
~String()...con=45678
创建对象 s1
创建对象 s2
传递形参 s2
返回 return *this
释放 *this
释放形参 s2
释放对象 s2
释放对象 s1
eg 10-7 重载输入输出运算符
#include
#include
#include
using namespace std;
class MatrixOper // 声明运算符函数,重载 >> << + - *
{
private:
int row;
int col;
double *Value;
public:
MatrixOper(int r, int c) // 构造函数
{
row = r;
col = c;
if(row<0 || col<0)
{
cout << "Invalidate row or col" << endl;
return ;
}
Value = new double[r * c];
memset(Value, 0, sizeof(double)*c*r);
}
~MatrixOper() //析构函数
{
if(this->Value)
{
delete[] Value;
Value = nullptr;
}
return;
}
void InitMatrix()
{
cout << "please input the matrix data." << endl;
cin >> *this;
}
friend istream& operator>>(istream& in, MatrixOper& m); // 重载 >>
friend ostream& operator<<(ostream& out, MatrixOper& m); // 重载 <<
friend MatrixOper& operator+(MatrixOper& m1, MatrixOper& m2); // 重载 +
friend MatrixOper& operator-(MatrixOper& m1, MatrixOper& m2); // 重载 -
friend MatrixOper& operator*(MatrixOper& m1, MatrixOper& m2); // 重载 *
MatrixOper& operator=(MatrixOper&); // 重载 =
double& operator()(int r, int c); // 重载 ()
};
istream& operator>>(istream& in, MatrixOper& m)
{
int index = 0;
//cout << m.row << " " << m.col << endl;
while(index<m.row * m.col)
{
in>>m.Value[index];
//cout<< "hello" << m.Value[index] << endl;
index++;
}
return in;
}
ostream& operator<<(ostream& out, MatrixOper& m)
{
// cout << m.row << " " << m.col << endl;
for(int i=1; i<=m.row; i++)
{
for(int j=1; j<=m.col; j++)
{
out<<setw(5)<<m(i,j);
}
cout << endl;
}
return out;
}
MatrixOper& operator+(MatrixOper& m1, MatrixOper& m2)
{
static MatrixOper result(m1.row, m1.col); // static,退出函数后,对象没有被释放,还在
if((m1.row != m2.row) || (m1.col != m2.col))
{
cout << "The two matrix cannot be added." << endl;
}
else
{
for(int i=1; i<=m1.row; i++)
{
for(int j=1; j<=m1.col; j++)
{
result(i,j) = m1(i,j) + m2(i,j);
}
}
}
return result;
}
MatrixOper& operator-(MatrixOper& m1, MatrixOper& m2)
{
static MatrixOper result(m1.row, m1.col); // static,退出函数后,对象没有被释放,还在
if((m1.row != m2.row) || (m1.col != m2.col))
{
cout << "The two matrix cannot be match." << endl;
}
else
{
for(int i=1; i<=m1.row; i++)
{
for(int j=1; j<=m1.col; j++)
{
result(i,j) = m1(i,j) - m2(i,j);
}
}
}
return result;
}
MatrixOper& operator*(MatrixOper& m1, MatrixOper& m2) // 矩阵乘法
{
static MatrixOper result(m1.row, m2.col);
for(int i=1; i<=m1.row; i++)
{
for(int j=1; j<=m2.col; j++)
{
double tmp = 0;
for(int k=1; k<=m1.col; k++)
tmp += m1(i,k) * m2(k,j);
result(i,j) = tmp;
}
}
return result;
}
MatrixOper& MatrixOper::operator=(MatrixOper& m)
{
if((m.row != row) || (m.col != col))
{
cout << "The two matrix cannot be match." << endl;
}
else
{
for(int i=1; i<=m.row; i++)
{
for(int j=1; j<=m.col; j++)
{
Value[(i-1)*col + (j-1)] = m(i,j);
// this->Value[(i-1)*col + (j-1)] = m(i,j);
}
}
}
return *this;
}
double& MatrixOper::operator()(int r, int c)
{
if((r>=1 && r<=row) &&(c>=1 && c <=col))
{
//cout << "Value:" << Value[(r-1)*col + (col -1)] << endl;
return Value[(r-1)*col + (c -1)];
}
else
return Value[0];
}
int main()
{
MatrixOper a(2,2), b(2,2), c(2,3);
a.InitMatrix();
b.InitMatrix();
cout << "The Data of matrix A is:" << endl << a;
cout << "The Data of matrix B is:" << endl << b;
cout << "The Data of matrix A+B=" << endl << a+b;
cout << "The Data of matrix A-B=" << endl << a-b;
cout << "The Data of matrix AxB=" << endl << a*b;
a = b;
cout << "The Data of matrix A is:" << endl << a;
return 0;
}
output
please input the matrix data.
1 2 3 4
please input the matrix data.
5 6 7 8
The Data of matrix A is:
1 2
3 4
The Data of matrix B is:
5 6
7 8
The Data of matrix A+B=
6 8
10 12
The Data of matrix A-B=
-4 -4
-4 -4
The Data of matrix AxB=
19 22
43 50
The Data of matrix A is:
5 6
7 8
重载了 >> << + - * = ()
return 返回了引用,eg friend MatrixOper& operator-(MatrixOper& m1, MatrixOper& m2);
矩阵用的一维数组存储,注意程序中矩阵的下标表示方式 m(1,1) 等价于 Value[0],通过重载 () 实现的转换
矩阵的加法和减法实现基本一样
矩阵的乘法需要 3 层循环实现
重载 = 时,this->Value[(i-1)*col + (j-1)] = m(i,j);
等价于 Value[(i-1)*col + (j-1)] = m(i,j);
重载 = 还可以利用 memcpy
实现,具体如下
MatrixOper& MatrixOper::operator=(MatrixOper& m)
{
if (this== &m)
{
cout << "This is the same object" << endl;
}
else
{
row = m.row;
col = m.col;
memcpy(Value, m.Value, sizeof(double)*m.row*m.col);
}
return *this;
}
运算符重载就是函数重载
我们重载 + 后,只是多了一种 + 的实现,原本 + 的功能还是存在的
更多有趣的代码示例,可参考【Programming】