这种方法相对麻烦,主要是在两个类之间的前后声明有着复杂的逻辑关系。但只要我们理清思路,是可以实现的。跟着我从最初的想法到实现一步一步的理顺逻辑,就非常容易理解如何操作了。
首先,我们要实现让 ManagerPoint 类中的成员函数 distance() 操作 Point 类中的私有数据成员_x和_y,所以要将 ManagerPoint 类中的 distance() 函数在 Point 类中声明为友元函数。如下代码:
代码语言:javascript复制#include
using namespace std;
class Point
{
public:
Point(int xx, int yy)
{
_x = xx;
_y = yy;
}
void display()
{
cout << “(“ << _x << “,” << _y << “)” << endl;
}
// 将 ManagerPoint 中的 distance 函数声明为友元函数
// 找不到 ManagerPoint 类!
friend ManagerPoint::distance(Point& a, Point& b);
private:
int _x;
int _y;
};
class ManagerPoint
{
public:
// 尝试访问 Point 类中的私有成员 失败!
int distance(Point& a, Point& b)
{
int dx = a._x - b._x;
int dy = a._y - b._y;
return sqrt(dx * dx dy * dy);
}
};
int main(int argc, char* argv[])
{
Point p1(3, 4), p2(6, 8);
p1.display();
p2.display();
ManagerPoint mp;
int d = mp.distance(p1,p2);
cout << “Distance is : “ << d << endl;
getchar();
return 0;
}
但这样的代码编译时你会发现,Point 类因为找不到 ManagerPoint 类的声明,编译时会报错,提示ManagerPoint 不是类或命名空间:
因为 ManagerPoint 类是在 Point 类之后声明定义的,所以他找不到,这样我们需要将 ManagerPoint 类的声明和定义移动到 Point 类之前。移动后的代码如下:
代码语言:javascript复制#include
using namespace std;
class ManagerPoint
{
public:
// 无法识别 Point 是一个什么类型,因为这段代码前并没有声明或定义 !
int distance(Point& a, Point& b)
{
int dx = a._x - b._x;
int dy = a._y - b._y;
return sqrt(dx * dx dy * dy);
}
};
class Point
{
public:
Point(int xx, int yy)
{
_x = xx;
_y = yy;
}
void display()
{
cout << “(“ << _x << “,” << _y << “)” << endl;
}
// 将 ManagerPoint 中的 distance 函数声明为友元函数
friend ManagerPoint::distance(Point& a, Point& b);
private:
int _x;
int _y;
};
int main(int argc, char* argv[])
{
Point p1(3, 4), p2(6, 8);
p1.display();
p2.display();
ManagerPoint mp;
int d = mp.distance(p1,p2);
cout << “Distance is : “ << d << endl;
getchar();
return 0;
}
但这样编译又会报错,因为你在 ManagerPoint 类中使用了Point类,很明显,在ManagerPoint之前并没有声明或定义Point类,那么我们可以使用一种手段叫做“前向声明”的方式,将 Point 类声明在 ManagerPoint 类之前。(前向型声明又称为不完全型声明,只能骗过引用或指针,因为引用或指针都是固定大小的,只要在需要的位置给其留下固定大小的空间即可,但如果 ManagerPoint 类中的 distance() 函数是值传递的 Point 的对象,就必须要计算出 Point 类所占用空间的大小,因为只做了简单的前向声明,不清楚 Point 类中都有什么成员,是无法计算出 Point 类的大小的,所以前向声明是骗不过值传递的)代码如下:
代码语言:javascript复制#include
using namespace std;
// 前向声明 Point 类
class Point;
class ManagerPoint
{
public:
// 尝试访问 Point 类中的私有成员 失败!
int distance(Point& a, Point& b)
{
int dx = a._x - b._x;
int dy = a._y - b._y;
return sqrt(dx * dx dy * dy);
}
};
class Point
{
public:
Point(int xx, int yy)
{
_x = xx;
_y = yy;
}
void display()
{
cout << “(“ << _x << “,” << _y << “)” << endl;
}
// 将 ManagerPoint 中的 distance 函数声明为友元函数
friend ManagerPoint::distance(Point& a, Point& b);
private:
int _x;
int _y;
};
int main(int argc, char* argv[])
{
Point p1(3, 4), p2(6, 8);
p1.display();
p2.display();
ManagerPoint mp;
int d = mp.distance(p1,p2);
cout << “Distance is : “ << d << endl;
getchar();
return 0;
}
这样是不是就万事大吉了?? 不,还没那么简单,这样编译后,又出现了新问题,如下图:
编译器提示,Point是一个未定义的类,因为我们直接操作了Point类中的_x和_y成员,而我们之前只给出了Point类的一个前向声明,并没有告诉它Point类中都有什么成员。所以这里操作时依然是行不通的。那难道就这样罢手了吗? 换位思考一下,当我们将前 Point 类做了前向声明后,ManagerPoint类中的 distance() 函数已经可以识别 Point 这个类型了,也就是说,如果我们这里只做 distance() 函数的声明,而把distance() 函数的实现放到 Point 类的后面,是不是就可以避免以上所有遇到的问题了呢?
代码语言:javascript复制#include
using namespace std;
// 前向声明 Point 类
class Point;
class ManagerPoint
{
public:
// 只做声明,这里已经可以通过前向声明的Point识别Point类型
int distance(Point& a, Point& b);
};
class Point
{
public:
Point(int xx, int yy)
{
_x = xx;
_y = yy;
}
void display()
{
cout << “(“ << _x << “,” << _y << “)” << endl;
}
// 将 ManagerPoint 中的 distance 函数声明为友元函数
friend ManagerPoint::distance(Point& a, Point& b);
private:
int _x;
int _y;
};
// 声明在前,定义在后,将distance方法在Point类后面定义
// 不但解决了找不到Point类型的问题,而且还知道了Point类中都具有什么成员
int ManagerPoint::distance(Point& a, Point& b)
{
int dx = a._x - b._x;
int dy = a._y - b._y;
return sqrt(dx * dx dy * dy);
}
int main(int argc, char* argv[])
{
Point p1(3, 4), p2(6, 8);
p1.display();
p2.display();
ManagerPoint mp;
int d = mp.distance(p1,p2);
cout << “Distance is : “ << d << endl;
getchar();
return 0;
}
不错,以上代码可以顺利编译通过,我们成功的解决了两个类之间,某一个类的成员函数作为另外一个类的友元函数的实现。 对比一下最初的代码,我们做了3处的改动,如下图: