曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 2976|回复: 0
打印 上一主题 下一主题

非成员运算符函数

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34387
跳转到指定楼层
楼主
发表于 2011-8-18 12:49:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
成员函数可以重载运算符;非成员函数也可以重载一个类的运算符,该函数通常是一个友员。

非成员函数(包括友员函数)是没有 this 指针参数的,因此当友员函数被用来重载二元运算符时,两个操作数都应该被显式地传递到函数中,而在重载一元函数时,则需要传递一个操作数。

不能使用非成员函数重载的运算符有:
=, (), [], ->

示例程序:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class three_d {
    int x, y, z;    //3个坐标值
public:
    three_d() { x = y = z = 0; }
    three_d (int i, int j, int k) { x = i; y = j; z = k; }

    friend three_d operator+(three_d op1, three_d op2);    //友员函数重载运算符
    three_d operator=(three_d op2);    //成员函数重载运算符

    void show();
};

//使用友员函数重载运算符
three_d operator+(three_d op1, three_d op2)
{
    three_d temp;

    temp.x = op1.x + op2.x;
    temp.x = op1.y + op2.y;
    temp.x = op1.z + op2.z;

    return temp;
}

three_d three_d::operator=(three_d op2)
{
    x = op2.x;
    y = op2.y;
    z = op2.z;

    return *this;
}

void three_d::show()
{
    cout << x << ", ";
    cout << y << ", ";
    cout << z << "\n";
}

int main()
{
    three_d a(1, 2, 3), b(10, 10, 10), c;

    a.show();
    b.show();

    c = a + b;
    c.show();

    c = a + b + c;
    c.show();

    c = b = a;    //多重赋值运算
    c.show();
    b.show();

    return 0;
}

运行输出:
$ ./friendop
1, 2, 3
10, 10, 10
13, 0, 0
0, 0, 0
1, 2, 3
1, 2, 3
在许多情况下,使用友员函数来重载运算符并不会比使用成员函数好多少。但是在一种情况下使用友员函数却非常有用:当你想在二元运算符的左边使用内置类型的对象时。

我们知道,当对象调用成员函数时,对象的 this 指针被作为参数传递给函数。在二元运算中,是运算符左边的对象调用了运算符函数。也就是说,如果是运算符左边的对象重载了运算符函数,那么是没有什么问题的。假设某个对象 ob 的类中重载了整数的加法,那么 ob + 10; 这个表达式完全合法,如下面程序所示:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class myclass {
    int count;
public:
    myclass() { count = 0; }
    myclass(int i) { count = i; }

    myclass operator+(myclass op2);
    myclass operator=(myclass op2);
    myclass operator+(int i);
    

    int retval() { return count; };
    void show();
};

myclass myclass::operator+(myclass op2)
{
    myclass temp;

    temp.count = count + op2.count;

    return temp;
}

myclass myclass::operator+(int i)
{
    myclass temp;
    temp.count = count + i;
    return temp;
}

myclass myclass::operator=(myclass op2)
{
    count = op2.count;

    return count;
}

void myclass::show()
{
    cout << count << "\n";
}

int main()
{
    myclass a(12), b(13), c;

    a.show();
    b.show();
    c = a + b;
    c.show();


    c = b + 30; 
    c.show();

    return 0;
}


运行输出:
$ ./temp
12
13
25
43
在上面程序中,我们重载了一个 '+' 运算符且带有整数参数的函数,所以在最后的 c = b + 30; 这条语句没有任何问题。但是 c = 30 + b; 这条语句却不能执行,它的问题在于:运算符 '+' 左边的对象是整数,而内置数据类型并没有定义整数和对象 b 的加法运算。简单的说,就是整数 10 和 this 指针没啥联系。

对于上述问题,解决的方法是使用两个友员函数来重载运算符 '+' 。在这种情况下,运算符两边的两个操作数都被作为参数显式地传递给函数,就像调用其他重载函数一样,编译器将根据参数的类型选择合适的重载函数。在上面的两个运算符 '+' 的重载函数中,一个用来处理 "对象 +整数" ,另一个则用来处理 "整数 + 对象“ 的运算。

使用友员函数来重载运算符 '+' (或其它的二元运算符),允许内置数据类型放在运算符的左边或右边。下面程序示例这种情况:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class CL {
public:
    int count;
    CL operator=(CL obj);
    friend CL operator+(CL ob, int i);
    friend CL operator+(int i, CL ob);
};

CL CL::operator=(CL obj)
{
    count = obj.count;
    return *this;
}

//处理 "对象+整数" 运算
CL operator+(CL ob, int i)
{
    CL temp;
    temp.count = ob.count + i;
    return temp;
}

//处理 "整数+对象" 运算
CL operator+(int i, CL ob)
{
    CL temp;
    temp.count = ob.count + i;
    return temp;
}

int main()
{
    CL O;
    O.count = 10;
    cout << O.count << " ";        //输出结果 10

    O = 10 + O;            //整数 + 对象
    cout << O.count << " ";        //输出结果 20

    O = O + 12;            //对象 + 整数
    cout << O.count;        //输出 32

    return 0;
}

运行输出:
$ ./mulop
10 20 32
在上面程序中,函数 operator+() 被重载了两次,这样整数 和 CL 类型的对象能够进行两种形式的加法运算。

使用友员函数重载一元运算符
也可以用友员函数来重载一元运算符。但是对于重载二元运算符而言,需要做一些额外的工作。

在我们使用成员函数来重载运算符 ++ 时,如下面代码所示:
[C++] 纯文本查看 复制代码
three_d three_d::operator++()
{
   x++;
   y++;
   z++;
   return *this;
}

我们知道,函数中并不需要指定参数,因为每个成员函数都接收一个隐式的参数 this 指针,这个指针指向调用函数的对象。所以在这种情况下,函数的惟一参数是一个指向调用函数的对象的指针。在函数中任何对参数的修改都将影响到调用运算符函数的对象。所以像上面的代码中,x++ 将增加调用函数的对象成员 x 的值。

与成员函数不同的是,非成员函数(包括友员函数),并不会接收隐式的 this 指针作为参数。所以,在函数中也就不能访问调用函数的对象。实际上,我们需要将参数显式地传递给友员运算符函数。因此,下面代码中的函数 operator++() 将不能改变对象参数的值:
[C++] 纯文本查看 复制代码
three_d operator++(three_d op1)
{ 
   op1.x++;
   op1.y++;
   op1.z++;
   return op1;
}

上面这个函数不能修改对象 op1 的值,因为这里的参数只是对象 op1 的一个副本通过参数 op1 被传递给函数,而不是对象的自身。所以,在函数 operator++() 中对参数所做的任何改变都不会影响调用函数的对象,因此参数只是个局部变量。

如果希望用友员函数来重载增量运算符或减量运算符,那么必须把对象作为一个引用传递给函数。由于引用是指向参数的隐式指针,所以对参数的改变将会影响调用函数的实际参数。

当使用友员函数来重载增量或减量运算符时,运算符的前缀形式带有一个参数(也就是操作数),而后缀形式则带有 2 个参数,其中第 2 个参数是个整数,仅是用于区别前缀形式和后缀形式,在函数中不会被使用到。

下面是示例程序:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

class three_d {
    int x, y, z;    //3个坐标值
public:
    three_d() { x = y = z = 0; }
    three_d (int i, int j, int k) { x = i; y = j; z = k; }

    friend three_d operator+(three_d op1, three_d op2);    //友员函数重载运算符
    three_d operator=(three_d op2);    //成员函数重载运算符

    //在运算符 ++ 的重载函数中使用引用参数
    friend three_d operator++(three_d &op1);
    friend three_d operator++(three_d &op1, int notused);

    void show();
};

//使用友员函数重载运算符
three_d operator+(three_d op1, three_d op2)
{
    three_d temp;

    temp.x = op1.x + op2.x;
    temp.x = op1.y + op2.y;
    temp.x = op1.z + op2.z;

    return temp;
}

//重载运算符=
three_d three_d::operator=(three_d op2)
{
    x = op2.x;
    y = op2.y;
    z = op2.z;

    return *this;
}

//使用友员函数来重载运算符++的前缀形式,函数中要求使用引用参数
three_d operator++(three_d &op1)
{
    op1.x++;
    op1.y++;
    op1.z++;

    return op1;
}

//使用友员函数来重载运算符++的后缀形式,函数中要求使用引用参数
three_d operator++(three_d &op1, int notused)
{
    three_d temp = op1;

    op1.x++;
    op1.y++;
    op1.z++;

    return temp;
}


void three_d::show()
{
    cout << x << ", ";
    cout << y << ", ";
    cout << z << "\n";
}

int main()
{
    three_d a(1, 2, 3), b(10, 10, 10), c;

    a.show();
    b.show();

    c = a + b;
    c.show();

    c = a + b + c;
    c.show();

    c = b = a;    //多重赋值运算
    c.show();
    b.show();

    ++c;        //前缀增量运算符
    c.show();

    c++;
    c.show();    //后缀增量运算符

    a = ++c;    //a 得到 c 做增量运算后的值
    a.show();
    c.show();    //a 和 c 的值是相同的

    a = c++;    //a 得到 c 做增量运算前的值
    a.show();
    c.show();    //a 与 c 的值是不同的


    return 0;
}

运行输出:
$ ./friendop2
1, 2, 3
10, 10, 10
13, 0, 0
0, 0, 0
1, 2, 3
1, 2, 3
2, 3, 4
3, 4, 5
4, 5, 6
4, 5, 6
4, 5, 6
5, 6, 7

注意:在一般情况下,应该使用成员函数来重载运算符,而使用友员函数重载运算符通常是用来处理某些特殊情况的。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-5-21 10:03 , Processed in 0.068206 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表