必备技能9.6:友元函数
通常情况下,只有类的成员才能够访问类的私有成员。然而,我们可以通过声明一个函数为类的友元函数来允许这个非成员函数访问类的私有成员。这个是通过在类的共有成员区引入该函数的原型,并在前面加上friend关键字来完成的。例如,下面的代码段中,frnd()就被声明成是类MyClass的一个友元函数:
class MyClass
{
//
public:
friend void frnd(MyClass ob);
//
};
可以看出,关键字friend是位于最前的。一个函数可以是多个类的友元函数。下面就是一个简短的示例程序,其中由友元函数来判断MyClass类的两个私有字段是否有公约数:
//友元函数示例
#include <iostream>
using namespace std;
class MyClass
{
int a, b;
public:
MyClass ( int i, int j )
{
a = i;
b = j;
}
friend int comDenom(MyClass x); // 友元函数
};
//注意:comDenom并不是类的成员函数
int comDenom(MyClass x )
{
/*由于comDenom是友元函数,所以它可以直接访问类的成员a和b */
int max = x.a < x.b ? x.a : x.b;
for ( int i = 2; i <= max; i++)
{
if ( (x.a % i == 0 ) && (x.b % i == 0 ) )
{
return i;
}
}
return 0;
}
int main()
{
MyClass n(18,111);
if (comDenom((n)))
{
cout <<"Common denominator is " << comDenom(n) << "\n";
}
else
{
cout << "No common denominator. \n";
}
return 0;
}
在这个示例中,函数comDenom()并不是类MyClass的成员函数。然而,它却可以完全访问类MyClass的私有成员。更明确地说,它可以完全访问x.a和x.b。还需要注意的是comDenom()的调用和普通函数的调用是一样的。前面不需要使用类的名称(其实,前面是不能使用类的名称的)。一般情况下,友元函数会被传入一个或者多个对象来作为友元函数的参数,就像上面的comDenom()一样。
从上面的示例程序来看,把函数comDenom()作为类MyClass的友元函数和作为类的成员函数相比并没有什么明显的好处。实际中,在有些情况下,友元函数是相当有用的。其一,友元函数可以被用来重载某些运算符,这点我们在本章的后面会进行讨论。其二,友元函数简化了某些输出输出函数的创建,这点我们在第十一章中会进行讨论。
使用友元函数的第三种好处就是:在某些情况下,两个或者多个类的成员在程序的某些部分是相互有关联的。例如,假设有两个类Cube和Cylinder用来定义立方体和圆柱体的一些特性,其中就有一个是颜色。为了能对立方体和圆柱体的颜色进行方便地比较,我们可以定义了一个友元函数来比较两个对象的颜色。如果两个对象的颜色相同,函数返回true,否则就返回false。下面的程序演示了这种想法:
//两个或者多个类可以共享同一个友元函数
#include <iostream>
using namespace std;
class Cylinder; // 前置声明
enum colors
{
red,
green,
yellow
};
class Cube
{
colors color;
public:
Cube(colors c)
{
color = c;
}
friend bool sameColor(Cube x, Cylinder y);
//....
};
class Cylinder
{
colors color;
public:
Cylinder(colors c)
{
color = c;
}
friend bool sameColor(Cube x, Cylinder y);
//....
};
bool sameColor(Cube x, Cylinder y)
{
if (x.color == y.color )
{
return true;
}
else
{
return false;
}
}
int main()
{
Cube cube1(red);
Cube cube2(green);
Cylinder cyl(green);
if (sameColor(cube1, cyl))
{
cout << "cube1 and cyl are the same color.\n";
}
else
{
cout << "cube1 and cyl are the different color.\n";
}
if (sameColor(cube2, cyl))
{
cout << "cube2 and cyl are the same color.\n";
}
else
{
cout << "cube2 and cyl are the different color.\n";
}
return 0;
}
上面程序的输出如下:
cube1 and cyl are the different color.
cube2 and cyl are the same color.
注意:在上面的这个程序中我们使用到了类Cylinder的前置声明(也叫做前置引用)。之所以这样做是因为在Cube类中的函数sameColor()引用到了该类,而之前,该并没有声明该类。创建类的前置声明就是采用上面示例程序中的写法。
一个类的友元函数还可以是另外类的成员函数。例如,下面的程序是对上述程序的重写,其中sameColor()是作为类Cube的成员函数出现的。注意:这里在声明sameColor()作为Cylinder的友元函数的时候需要使用到作用域解析运算符。
/*一个函数可以是一个类的成员函数,同时又是另外一个类的友元函数 */
#include <iostream>
using namespace std;
class Cylinder; //前置声明
enum colors
{
red,
green,
yellow
};
class Cube
{
colors color;
public:
Cube(colors c)
{
color = c;
}
bool sameColor (Cylinder y);
};
class Cylinder
{
colors color;
public:
Cylinder(colors c)
{
color = c;
}
// Cube::sameColor()是Cylinder类的友元函数
friend bool Cube::sameColor(Cylinder y);
};
bool Cube::sameColor(Cylinder y)
{
if (color == y.color )
{
return true;
}
else
{
return false;
}
}
int main()
{
Cube cube1(red);
Cube cube2(green);
Cylinder cyl(green);
if (cube1.sameColor(cyl))
{
cout << "cube1 and cyl are the same color.\n";
}
else
{
cout << "cube1 and cyl are the different color. \n";
}
if (cube2.sameColor(cyl))
{
cout << "cube2 and cyl are the same color. \n";
}
else
{
cout << "cube2 and cyl are the different color. \n";
}
}
在这个程序中,由于sameColor()是Cube的成员函数,所以在调用的时候必须是通过一个Cube类的对象来调用。这也就意味着在函数中是可以直接访问Cube类的成员变量color的。因此,我们只需要为其传入Cylinder类的对象即可。
练习
1. 什么是友元函数?声明友元函数时用到那个关键字?
2. 通过对象来使用友元函数时要使用点号(.)运算符?
3. 友元函数是否可以是另外一个类的成员函数?