这几天在复习数学考试,都没有学C++,今天抽空来学一点。
什么是友元?
- 友元是C++中的一种关系
- 友元发生在函数与类之间或者类与类之间
- 友元关系是单向的,不能传递
在具体讲解友元的性质之前,我们先来看看一个程序,这个程序是计算两点之间的距离:
#include <stdio.h>
#include <math.h>
class Point
{
private:
double x;
double y;
public:
Point(double x,double y)
{
this->x = x; //this指针指向当前的对象
this->y = y;
}
double getX()
{
return x;
}
double getY()
{
return y;
}
};
double func(Point& p1,Point& p2)
{
double ret = 0;
ret = (p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) +
(p1.getY() - p2.getY()) * (p1.getY() - p2.getY());
ret = sqrt(ret);
return ret;
}
int main()
{
Point p1(1,2);
Point p2(10,20);
printf("p1(%f,%f)\n",p1.getX(),p1.getY());
printf("p2(%f,%f)\n",p2.getX(),p2.getY());
printf("|p1-p2| = %f\n",func(p1,p2));
return 0;
}
上面代码运行结果为:
p1(1.000000,2.000000)
p2(10.000000,20.000000)
|p1-p2| = 20.124612
看着也没什么大问题。下面我们来简单分析一下这个程序的实用性:进行计算这个两点之间的距离时的代码为:
ret = (p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) +
(p1.getY() - p2.getY()) * (p1.getY() - p2.getY());
进行了8次函数的调用!!!这么简单的计算,就需要调用8次函数,这效率之慢在当初C++诞生的时候,是不被大多数人接受的,大多数人用C编程习惯了,都是可以直接这样调用的:
ret = (p1.x - p2.x) * (p1.x - p2.x) +
(p1.y - p2.y) * (p1.y - p2.y);
然而C++里面你就不能像上面那样直接调用类(C里面可以是结构体或者数组)的成员变量。因为它是private类型的变量。那么在C++诞生的那个年代,人们为了让C++语言完全兼容C语言,就在C++中加入了友元的存在。那么友元的定义以及性质是什么样的呢?
- 在类中以friend关键字声明友元
- 类的友元可以是其他类或者具体的函数
- 友元不是类的一部分
- 友元不受类中访问级别的限制
- 友元可以直接访问具体类的所有成员
直接将上面的代码修改一下看看:
#include <stdio.h>
#include <math.h>
class Point
{
private:
double x;
double y;
public:
Point(double x,double y)
{
this->x = x; //this指针指向当前的对象
this->y = y;
}
double getX()
{
return x;
}
double getY()
{
return y;
}
friend double func(Point& p1,Point& p2);
};
double func(Point& p1,Point& p2)
{
double ret = 0;
ret = (p1.x - p2.x) * (p1.x - p2.x) +
(p1.y - p2.y) * (p1.y - p2.y);
ret = sqrt(ret);
return ret;
}
int main()
{
Point p1(1,2);
Point p2(10,20);
printf("p1(%f,%f)\n",p1.getX(),p1.getY());
printf("p2(%f,%f)\n",p2.getX(),p2.getY());
printf("|p1-p2| = %f\n",func(p1,p2));
return 0;
}
运行结果为:
p1(1.000000,2.000000)
p2(10.000000,20.000000)
|p1-p2| = 20.124612
这样看来,我们已经实现了直接访问类的私有成员变量。上面的代码,func函数是Point类的友元,可以直接访问Point的所有数据。但是这样做,又不好,为什么呢?
友元的尴尬:
- 友元是为了兼顾C语言的高效性而诞生的
- 友元直接破坏了面向对象的封装性
- 友元在实际产品中的高效是得不偿失的
- 友元在现代软件工程中已经被逐渐遗弃
虽然基于以上的尴尬让友元这个功能在现代软件工程中很少被使用,但是我们学习嘛,肯定是都要学的,哈哈~
友元需要注意的一些事项:
- 友元关系不具备传递性
- 类的友元可以是其他类的成员函数
- 类的友元可以是某个完整的类(所有的成员函数都是友元)
下面我们再分析一个代码,来看看友元的真实面目:
#include <stdio.h>
class ClassC
{
const char* n;
public:
ClassC(const char* n)
{
this->n = n;
}
friend class ClassB;
};
class ClassB
{
const char* n;
public:
ClassB(const char* n)
{
this->n = n;
}
void getClassCName(ClassC& c)
{
printf("c.n = %s\n", c.n);
}
friend class ClassA;
};
class ClassA
{
const char* n;
public:
ClassA(const char* n)
{
this->n = n;
}
void getClassBName(ClassB& b)
{
printf("b.n = %s\n", b.n);
}
/* void getClassCName(ClassC& c) { printf("c.n = %s\n", c.n); } */
};
int main()
{
ClassA A("A");
ClassB B("B");
ClassC C("C");
A.getClassBName(B);
B.getClassCName(C);
return 0;
}
运行结果为:
b.n = B
c.n = C
分析代码知:类B是C的友元,类A是B的友元,所以在B中可以直接访问C的私有成员变量,在A中可以直接访问B的成员变量。 那么在A中能否直接访问C呢?试验一下将上面的代码注释掉得部分恢复,编译运行,运行结果为:
test.cpp: In member function ‘void ClassA::getClassCName(ClassC&)’:
test.cpp:6: error: ‘const char* ClassC::n’ is private
test.cpp:44: error: within this context
很显然,A是不能访问C的私有成员的。
总结一下:
- 友元是为了兼顾C语言的高效而诞生的
- 友元直接破坏了面向对象的封装性
- 友元关系不具备传递性
- 类的友元可以是其他类的成员函数
- 类的友元可以是某个完整的类
想获得各种学习资源以及交流学习的加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。