|
成员函数可以重载运算符;非成员函数也可以重载一个类的运算符,该函数通常是一个友员。
非成员函数(包括友员函数)是没有 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;
}
运行输出:在上面程序中,我们重载了一个 '+' 运算符且带有整数参数的函数,所以在最后的 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;
}
运行输出:在上面程序中,函数 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
注意:在一般情况下,应该使用成员函数来重载运算符,而使用友员函数重载运算符通常是用来处理某些特殊情况的。 |
|