这种方法相对麻烦,主要是在两个类之间的前后声明有着复杂的逻辑关系。但只要我们理清思路,是可以实现的。跟着我从最初的想法到实现一步一步的理顺逻辑,就非常容易理解如何操作了。
首先,我们要实现让 ManagerPoint 类中的成员函数 distance() 操作 Point 类中的私有数据成员_x和_y,所以要将 ManagerPoint 类中的 distance() 函数在 Point 类中声明为友元函数。如下代码:
#include<iostream> 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 类之前。移动后的代码如下:
#include<iostream> 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 类的大小的,所以前向声明是骗不过值传递的)代码如下:
#include<iostream> 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 类的后面,是不是就可以避免以上所有遇到的问题了呢?
#include<iostream> 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处的改动,如下图:
Pingback引用通告: 运算符重载使用它类对象作为参数 | myCode