这几天在复习数学考试,都没有学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语言,数据结构等技术问题。