【C++】运算符重载总结

运算符重载的概念和语法

重载,就是赋予新的含义,运算符重载和函数重载类似,同一个运算符可以有不同的功能。

运算符重载的方式就是定义一个函数,在函数体内实现想要的功能,当用到该运算符时,编译器就会自动调用这个函数。也就是说,运算符重载就是通过函数定义实现的,它本质上是函数重载。

返回值类型 operator 运算符名称(形参列表){
//TODO;
}

operator是关键字,专门用于定义重载运算符的函数。

重载运算符的规则:

1):并不是所有运算符都可以重载。例如长度运算符(sizeof)、条件运算符(: ?)、成员选择符(.)、对象选择符(.*)、域解析符(::)不能被重载。
2):重载不能改变运算符的优先级和结合性。
3):重载不会改变运算符的用法。例如"+"号总是出现在两个操作数之间。
4):重载运算符的函数不能有默认的参数,否则就改变了运算符操作数的个数,显然是错误的。

下面的代码定义了一个复数类,通过运算符重载,用”+”号实现了复数的加法。

/*************************************************************************
    > File Name: 运算符重载.cpp
    > Author: Tanswer_
    > Mail: 98duxm@gmail.com
    > Created Time: 2016年10月15日 星期六 19时49分34秒
 ************************************************************************/

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <stack>
#include <queue>
using namespace std;

class Complex
{
private:
    double real;    //实部
    double imag;    //虚部
public:
    Complex(): real(0.0),imag(0.0) {}
    Complex(double a,double b):real(a),imag(b) {}

    Complex operator +(const Complex & A) const; //类内声明+重载
    void display() const;
};

/*运算符重载*/
Complex Complex::operator+(const Complex & A) const
{
    /*简单的写法*/
    return Complex(real+A.real,imag+A.imag);
    /*一般写法*/
    /*Complex B;
    B.real = A.real + real;
    B.imag = A.imag + imag;
    return B;
    */
};



void Complex::display() const 
{
    cout << real << "+" << imag << "i" << endl;
};

int main()
{
    Complex a(1.1,2.2);
    Complex b(3.3,4.4);

    Complex c;
    c = b + a;
    c.display();
    return 0;
}

当执行c=a+b时,编译器检测到”+”号左边(左结合性)是一个Complex类对象时,就会调用该运算符重载函数,该语句被转换为:a.operator+(b)

用全局函数重载运算符

运算符重载函数既可以声明为类的成员函数,也可以声明为所有类之外的全局函数。

声明为类的成员函数

此时二元运算符只有一个,一元运算符不需要参数。之所以少一个参数,是因为这个参数是隐含的,正如上面解释的,当执行c=a+b时,编译器检测到”+”号左边(左结合性)是一个Complex类对象时,就会调用该运算符重载函数,该语句被转换为:a.operator+(b)。通过this指针隐式的访问a的成员变量。

声明为类之外的全局函数

将运算符重载函数声明为全局函数时,二元操作符就需要两个参数,一元操作符需要一个参数,而且其中必须有一个参数是对象,好让编译器区分这是程序员自定义的运算符,防止程序员修改用于内置类型的运算符的性质。
下面我们通过这种方法,实现复数的加减乘除。

/*************************************************************************
    > File Name: 运算符重载.cpp
    > Author: Tanswer_
    > Mail: 98duxm@gmail.com
    > Created Time: 2016年10月15日 星期六 20时58分34秒
 ************************************************************************/

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <stack>
#include <queue>
using namespace std;

class Complex
{
private:
    double real;    //实部
    double imag;    //虚部
public:
    Complex(): real(0.0),imag(0.0) {}
    Complex(double a,double b):real(a),imag(b) {}


    friend Complex operator*(const Complex & A,const Complex & B);
    friend Complex operator/(const Complex & A,const Complex & B);
    friend Complex operator+(const Complex & A,const Complex & B);
    friend Complex operator-(const Complex & A,const Complex & B);

    void display() const;
};

/* + 运算符重载*/
Complex operator+(const Complex & A,const Complex & B) 
{
    Complex C;
    C.real = A.real + B.real;
    C.imag = A.imag + B.imag;
    return C;
};

/* - 运算符重载*/
Complex operator-(const Complex & A,const Complex & B)
{
    Complex C;
    C.real = A.real - B.real;
    C.imag = A.imag - B.imag;
    return C;
};

/* * 运算符重载*/
Complex operator*(const Complex & A,const Complex & B)
{
    Complex C;
    C.real = A.real * B.real - A.imag * B.imag;
    C.imag = A.imag * B.real + A.real * B.imag;
    return C;
};

/* / 运算符重载*/
Complex operator/(const Complex & A,const Complex & B)
{
    Complex C;
    double square = A.real*A.real + A.imag*A.imag;
    C.real = (A.real * B.real + A.imag * B.imag)/square;
    C.imag = (A.imag * B.real - A.real * B.imag)/square;
    return C;
};

void Complex::display() const 
{
    cout << real << "+" << imag << "i" << endl;
};

int main()
{
    Complex a(1.1,2.2);
    Complex b(3.3,4.4);

    Complex c;

    c = a + b; 
    cout << "a+b= ";
    c.display();


    c = a - b;
    cout << "a-b= ";
    c.display();

    c = a*b;
    cout << "a*b= ";
    c.display();

    c = a/b;
    cout << "a/b= ";
    c.display();

    return 0;
}

输出结果:

a+b= 4.4 + 6.6i
a-b= -2.2 + -2.2i
a*b= -6.05 + 12.1i
a/b= 2.2 + 0.4i

两点说明:
1. 因为要用到类Complex的私有对象,所以将这几个全局函数声明为类的友元函数。
2. 指针操作符(->)、下标操作符([])、函数调用操作符(())、赋值操作符(=)只能以成员函数的形式重载。

重载<<和>>(输入和输出)
C++系统已经对”<<”和”>>”进行了重载,但是只能用于输入输出内置的数据类型。如果我们自己定义了一种新的数据类型,需要用输入输出去处理,那么就要重载。

下面我们用全局函数的形式重载输入运算符,使它读入两个double类型的数据,并转换为一个复数,保存到复数对象中。

istream & operator>>(istream &in, Complex &A)
{
    in >> A.real >> A.imag;
    return in;
};

istream是输入流,cin就是istream类的对象。我们还是把它声明为类的友元函数:friend istream & operator>>(istream &in,Complex &A);

在重载输入运算符时,采用引用的方式进行参数传递:输入的参数里面包含一个 istream 类的引用,返回值仍然为该引用。这样做的一个明显好处就是可以采用链式输入(也就是连续输入),如下所示:

complex a, b, c;
cin>> a >> b >> c;

同样的,和输入类似,我们重载输出运算符,使其能够输出复数。

ostream & operator<<(ostream & out,Complex &A)
{
    out << A.real << " + " << A.imag << "i";
    return out;
};

这样我们就能实现对类的对象直接输入输出操作了。

#include <iostream>
using namespace std;

class Complex
{
private:
    double real;    //实部
    double imag;    //虚部
public:
    Complex(): real(0.0),imag(0.0) {}
    Complex(double a,double b):real(a),imag(b) {}

    friend Complex operator +(const Complex & A,const Complex &B);
    /*输入输出运算符重载的声明*/
    friend istream & operator>>(istream &in,Complex &A);
    friend ostream & operator<<(ostream &out,Complex &A);

    friend Complex operator+(const Complex & A,const Complex & B);
    friend Complex operator-(const Complex & A,const Complex & B);
};

/* + 运算符重载*/
Complex operator+(const Complex & A,const Complex & B) 
{
    Complex C;
    C.real = A.real + B.real;
    C.imag = A.imag + B.imag;
    return C;
};

/* - 运算符重载*/
Complex operator-(const Complex & A,const Complex & B)
{
    Complex C;
    C.real = A.real - B.real;
    C.imag = A.imag - B.imag;
    return C;
};

istream & operator>>(istream &in, Complex &A)
{
    in >> A.real >> A.imag;
    return in;
};

ostream & operator<<(ostream & out,Complex &A)
{
    out << A.real << " + " << A.imag << "i";
    return out;
};

int main()
{
    Complex a,b,c;
    cin >> a >> b;

    c = b + a;
    cout << "a+b= " << c << endl;

    c = a - b;
    cout << "a-b= " << c << endl;

    return 0;
}

我们输入

1.1 2.2 3.3 4.4

输出:

a+b= 4.4 + 6.6i
a-b= -2.2 + -2.2i

重载[] (下标运算符)

默认情况下使用下标运算符并不具备检查边界溢出功能,我们可以通过重载实现此功能。

/*************************************************************************
    > File Name: 下标运算符重载.cpp
    > Author: Tanswer_
    > Mail: 98duxm@gmail.com
    > Created Time: 2016年10月16日 星期日 14时41分56秒
 ************************************************************************/

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <stack>
#include <queue>
using namespace std;

class Array
{
private:
    int length;
    int *num;

public:
    Array():length(0),num(NULL) {}
    Array(int n);

    int getLength() const { return length; }
    int & operator[](int);
    const int & operator[](int) const;

};

Array::Array(int n)
{
    num = new int[n];
    length = n;
};

/*这个能够修改对象*/
int & Array::operator[](int i)
{
    if(i >= length || i < 0)
        throw string("out of bounds!");
    return num[i];
};

/*常量成员函数,这个只能访问对象,不能修改*/
const int & Array::operator[](int i) const
{
    if(i >= length || i < 0)
        throw string("out of bounds!");
    return num[i];
};

int main()
{
    Array a(5);
    int i;
    try{
        for(i=0; i<a.getLength(); i++)
            a[i] = i;
        for(i=0; i<6; i++)
            cout << a[i] << endl;
    }catch(string s){
        cerr << s << ",i= " << i << endl;
    }
    return 0;
}

输出结果:

0
1
2
3
4
out of bounds!,i= 5

上面用到了string以及C++的异常处理,有疑问的可以跳过,重点是数组越界后产生异常了。a[5]会被转换为a.operator[](5)

重载=(赋值运算符)

对象之间也可以相互赋值,对象之间的赋值是将成员变量依次拷贝,而不是将整个对象的内存按位拷贝。一般情况下,默认的”=”就能满足我们的需求,但是当一个类中包含指针类型的成员变量时,可能会带来问题。

我们看下面的例子:

/*************************************************************************
    > File Name: 重载赋值运算符.cpp
    > Author: Tanswer_
    > Mail: 98duxm@gmail.com
    > Created Time: 2016年10月20日 星期四 13时19分03秒
 ************************************************************************/

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <stack>
#include <queue>
using namespace std;

class Book
{
public:
    Book():price(0.0),bookmark(NULL),num(0) {}
    Book(double, int *,int);
    void setbookmark(int,int);  //修改书签
    void display(); 

private:
    double price;   //书的价格
    int *bookmark;  //书签
    int num;        //书签的数量
};

Book::Book(double price,int *bm,int num):price(price),num(num)
{
    //对象有自己的数组,
    //在其他地方修改实参所指向的数组不会影响该数组
    int *bmtemp = new int[num];
    for(int i=0; i<num; i++)
        bmtemp[i] = bm[i];

    this -> bookmark = bmtemp;
}

void Book::setbookmark(int page,int index)
{
    if(index >= num-1)
        cout << "out of bound!" << endl;
    else
        bookmark[index] = page;
}

void Book::display()
{
    cout << "price:" << price <<endl;
    cout << "bookmarks: ";
    for(int i=0; i<num; i++)
    {
        if(i == num-1)
            cout << bookmark[i] << endl;
        else
            cout << bookmark[i] << ",";
    }
}

int main()
{
    int bookmark[] = { 1,49,56,290 };
    Book java,cpp(68.5,bookmark,4);
    cpp.display();

    java = cpp; //对象之间赋值
    java.setbookmark(100,2);
    cpp.display();

    return 0;
}

运行结果:

price:68.5
bookmarks: 1,49,56,290
price:68.5
bookmarks: 1,49,100,290

两次调用 display() 的结果不同表明,调用 java 对象的 setBookmark( ) 函数影响到了 cpp 对象。
上面程序中,当执行赋值语句时,将cpp.bookmark的值赋给java.bookmark,不同的对象的成员变量指向同一个数组,所以会相互影响。

要解决这个问题,我们就需要重载赋值运算符。


Book & Book::operator=(const Book & b)
{
    if(this != &b)
    {
        this -> price = b.price;
        this -> num = b.num;

        //为bookmark赋值
        int *bmtemp = new int[num];
        for(int i=0; i<num; i++)
            bmtemp[i] = b.bookmark[i];

        this -> bookmark = bmtemp;
    }
    return *this;
}

把重载函数放入Book类中,再执行赋值语句,会转换为 java.operator=(cpp) 。在函数体中,this 就指向 java 对象。这样 java 对象也会拥有属于自己的数组,两个对象之间不会再相会影响。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值