【C++】Operator Overloading

《C++程序设计基础教程》——刘厚泉,李政伟,二零一三年九月版,学习笔记


文章目录

  • 1、什么是运算符重载
  • 2、运算符重载规则
  • 3、运算符重载的实现形式
    • 3.1、重载为类的成员函数
    • 3.2、重载为友元函数
  • 4、应用实例


更多有趣的代码示例,可参考【Programming】


1、什么是运算符重载

在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)

2、运算符重载规则

C++中不是所有的运算符都可以重载,eg:

  • 条件运算符 ?:
  • 成员访问运算符 .
  • 作用域运算符 ::
  • sizeof
  • 成员指针运算符 .*

重载运算符时,不能改变运算符的优先级、结合性、操作数的数目,也不能改变运算符的语法语义

运算符重载有两种实现方式,分别为类的成员函数友元函数

只能使用类的成员函数形式重载的运算符有:=()[]->newdelete

单目运算符 ++-- 为了区别前置后置,重载的时候有如下区别

前置自增++

<类型>operator++()  // 作为类成员
<类型>operator++(<类型>)  // 作为友元函数

后置自增++

<类型>operator++(int)  // 作为类成员
<类型>operator++(<类型>, int)  // 作为友元函数

区别就在于这个 int 类型的参数(通常命名为 dummy实际上这个参数在函数体内不会被使用仅用于区分前置和后置自增运算符),dummy 的中文意思为笨蛋


调用方法

前置自增++

++a 或 a.operator++()  // 作为类成员
operator++(a) // 作为友元函数

后置自增++

a++ 或 a.operator++(0)// 作为类成员
operator++(a, 0) // 作为友元函数

3、运算符重载的实现形式

3.1、重载为类的成员函数

(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.012.012.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;
}

3.2、重载为友元函数

(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);

4、应用实例

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】

你可能感兴趣的:(C,/,C++,c++,开发语言,运算符重载,重载规则,友元函数)