Jamzy Wang

life is a struggle,be willing to do,be happy to bear~~~

C/C++ 运算符重载小结

2012-04-18 21:49

原创声明:本作品采用知识共享署名-非商业性使用 3.0 版本许可协议进行许可,欢迎转载,演绎,但是必须保留本文的署名(包含链接),且不得用于商业目的。

运算符重载的方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。也就是说,运算符重载是通过定义函数实现的。运算符重载实质上是函数的重载。

运算符重载函数的一般格式如下:

1
2
3
4
返回类型 类名::operator 操作符(形参表)
{
    //函数体
}

运算符重载的规则

C++对运算符重载定义了如下几条规则。

1) C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。

2) 重载不能改变运算符运算对象的个数。

3) 重载不能改变运算符的优先级别。

4) 重载不能改变运算符的结含性。如赋值运算符是右结合性(自右至左),重载后仍为右结合性。

5) 重载运算符的函数不能有默认的参数,否则就改变了运算符参数的个数,与前面第(2)点矛盾。

6) 重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。也就是说,参数不能全部是C++的标准类型,以防止用户修改用于标准类型数据的运算符的性质,下面这种重载方式是错误的,因为所有参数都是C++的标准类型:

1
2
3
4
int operator+(int a,int b)
{
    retum(a-b);
}

如果有两个参数,这两个参数可以都是类对象,也可以一个是类对象,一个是C ++标准类型的数据,如

1
2
3
4
5
A operator+(int a,A &b)
{
    A temp(b.data + a);
    return temp;
}

7) 用于类对象的运算符一般必须重载,但有两个例外,运算符“=”和“&”不必重载。

赋值运算符( = )可以用于每一个类对象,可以利用它在同类对象之间相互赋值。这是因为系统已为每一个新声明的类重载了一个赋值运算符,它的作用是逐个复制类的数据成员。用户可以认为它是系统提供的默认的对象赋值运算符,可以直接用于对象间的赋值,不必自己进行重载。但是有时系统提供的默认的对象赋值运算符不能满足程序的要求,例如,数据成员中包含指向动态分配内存的指针成员时,在复制此成员时就可能出现危险。在这种情况下, 就需要自己重载赋值运算符。

地址运算符&也不必重载,它能返回类对象在内存中的起始地址。

8) 应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能(如用“+”实现加法,用“>”实现“大于”的关系运算)。

9) 运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函敝的普通函数。

C++允许重载的运算符和不允许重载的运算符

  • 允许重载的运算符(C++中绝大部分的运算符允许重载):

此处输入图片的描述

“++”和“–”运算符有两种使用方式,前置自增运算符和后置自增运算符,它们的作用是不一样的,在重载时怎样区别这二者呢? 针对“++”和“–”这一特点,C++约定,在自增(自减)运算符重载函数中,增加一个int型形参,就是后置自增(自减)运算符函数。

  • 不能重载的运算符(5个):
1
2
3
4
5
.  (成员访问运算符)
.*  (成员指针访问运算符)
::  (域运算符)
sizeof  (长度运算符)
?:  (条件运算符)

前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征。

实现操作符重载的两种方式

1.友元函数重载操作符

1
2
3
4
class 类名
{
    friend 返回类型 operator 操作符(形参列表);
};

类外定义格式:

1
2
3
4
 返回类型 operator操作符(形参列表)
 {
     //函数体
 }

2.类成员函数实现操作符重载

1
2
3
4
5
class 类名
{
public:
    返回类型 operator 操作符(形参表)
};

类外定义格式

1
2
3
4
返回类型 类名::operator 操作符(形参表)
{
    //函数体
}

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A
{
public:
    A(int data)
    {
        this->data = data;
    };
    int getValue()
    {
        return this->data;
    }
    A operator+(A &a);  //成员函数重载
    friend A operator-(A &a, A &b);   //友元函数重载
    A operator++();  //前置自增
    A operator--(int); //后置自减
private:
    int data;
};

成员函数的实现如下:

A::operator+(A &a)

1
2
3
4
5
6
A A::operator+(A &a) //成员函数重载实现
{
    A temp = *this;
    temp.data = temp.data + this->data;
    return temp;
}

A::operator-(A &a, A &b)

1
2
3
4
5
6
A A::operator-(A &a, A &b) //友元函数重载实现
{
    int temp = a.getValue() - b.getValue();
    A c(temp);
    return c;
}

A::operator++()

1
2
3
4
5
6
A A::operator++() //前置自增
{
    A temp = *this;
    temp.data = temp.data + 1;
    return temp;
}

A::operator–(int)

1
2
3
4
5
6
A A::operator--(int) //后置自减
{
    A temp = *this;
    temp.data = temp.data - 1;
    return temp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char const *argv[]) {
    A a(10);
    A b(20);
    A c = a.operator+ (b);
    cout << c.getValue() << endl;
    A d = a - b;
    cout << d.getValue() << endl;
    A e = a--;
    cout << e.getValue() << endl;
    A f = ++a;
    cout << f.getValue() << endl;
    return 0;
}

程序输出:

1
2
3
4
20
-10
9
11

当运算符重载为类的成员函数时,函数的参数个数比原来的操作数要少一个(后置单目运算符除外),这是因为成员函数用this指针隐式地访问了类的一个对象,它充当了运算符函数最左边的操作数。因此:

(1) 双目运算符重载为类的成员函数时,函数只显式说明一个参数,该形参是运算符的右操作数。

(2) 前置单目运算符重载为类的成员函数时,不需要显式说明参数,即函数没有形参。

(3) 后置单目运算符重载为类的成员函数时,函数要带有一个整型形参。

(4)一般建议单目运算符重载为成员函数,而双目运算符重载为友元函数。

注:

友元函数实现的操作符重载是有限制的,比如:[] ,(),->和 =不能利用友元函数实现运算符的重载。

重载输入输出流

对“«”和“»”重载的函数形式如下:

1
2
istream & operator >> (istream &, 自定义类 &);
ostream & operator << (ostream &, 自定义类 &);

即重载运算符“»”的函数的第一个参数和函数的类型都必须是istream&类型,第二个参数是要进行输入操作的类。重载“«”的函数的第一个参数和函数的类型都必须是ostream&类型,第二个参数是要进行输出操作的类。因此,只能将重载“»”和“«”的函数作为友元函数或普通的函数,而不能将它们定义为成员函数。

1
2
3
4
5
ostream & operator << (ostream &output, A a)
{
    output << a.getValue() << endl;
    return output;
}
1
2
A a(10);
cout << a << endl;

Ref

Comments